synabun 2.0.10 → 2.2.0
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 +207 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +22 -0
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +13 -13
- package/hooks/claude-code/pre-websearch.mjs +12 -1
- package/hooks/claude-code/shared.mjs +5 -0
- package/hooks/claude-code/stop.mjs +1 -1
- package/lib/claude-hooks.js +172 -18
- package/lib/client-config-repair.js +30 -17
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +7 -6
- package/lib/first-launch-protection.js +57 -12
- package/lib/mcp-dist.js +176 -0
- package/lib/release-npm-ci.mjs +138 -0
- package/lib/release-publish.mjs +91 -0
- package/lib/release-scan.mjs +50 -0
- package/lib/server-crash-record.js +123 -0
- package/lib/server-exit.js +318 -0
- package/lib/server-supervisor.js +508 -0
- package/lib/start-launcher.js +37 -4
- package/lib/tools-cli.js +45 -0
- package/lib/update-snapshot.js +302 -0
- package/lib/virustotal.mjs +203 -0
- package/lib/windows-crash-event.js +269 -0
- package/mcp-server/dist/http.d.ts +20 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +59 -12
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts +33 -27
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +128 -143
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/preload.js +1 -1
- package/mcp-server/dist/preload.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/codex-config-heal.d.ts +6 -2
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +81 -28
- package/mcp-server/dist/services/codex-config-heal.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-judge-backfill.d.ts +6 -0
- package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
- package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
- 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 +18 -11
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +67 -11
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts +7 -2
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +22 -63
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
- package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-catalog.js +339 -0
- package/mcp-server/dist/services/tool-catalog.js.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
- package/mcp-server/dist/services/tool-list.d.ts +108 -0
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list.js +334 -0
- package/mcp-server/dist/services/tool-list.js.map +1 -0
- package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-runtime.js +400 -0
- package/mcp-server/dist/services/tool-runtime.js.map +1 -0
- 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/services/whiteboard-geometry.d.ts +26 -2
- package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
- package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
- package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +14 -14
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +29 -19
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
- 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/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.js +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 +505 -0
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill-tools.js +642 -0
- package/mcp-server/dist/tools/skill-tools.js.map +1 -0
- package/mcp-server/dist/tools/skill.d.ts +7 -0
- package/mcp-server/dist/tools/skill.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill.js +11 -0
- package/mcp-server/dist/tools/skill.js.map +1 -0
- package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
- package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
- package/mcp-server/dist/tools/tool-groups.js +147 -0
- package/mcp-server/dist/tools/tool-groups.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-format.js +7 -4
- package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/http.ts +75 -12
- package/mcp-server/src/index.ts +147 -154
- package/mcp-server/src/preload.ts +12 -12
- package/mcp-server/src/services/budget-judgments.ts +5 -3
- package/mcp-server/src/services/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +81 -21
- package/mcp-server/src/services/server-instructions.ts +29 -64
- package/mcp-server/src/services/tool-catalog.ts +403 -0
- package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
- package/mcp-server/src/services/tool-list.ts +410 -0
- package/mcp-server/src/services/tool-runtime.ts +457 -0
- package/mcp-server/src/services/typesafe-config.ts +1 -1
- package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +29 -20
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +592 -0
- package/mcp-server/src/tools/skill.ts +13 -0
- package/mcp-server/src/tools/tool-groups.ts +164 -0
- package/mcp-server/src/tools/whiteboard-format.ts +9 -3
- package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
- package/neural-interface/i18n/en.json +3953 -228
- package/neural-interface/i18n/pt-BR.json +3901 -179
- 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 +128 -28
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- package/neural-interface/lib/assistant-brains/claude.js +4 -3
- package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
- package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
- package/neural-interface/lib/assistant-brains/codex.js +9 -5
- package/neural-interface/lib/assistant-brains/opencode.js +120 -54
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
- 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 +81 -15
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +1073 -200
- package/neural-interface/lib/assistant-envelope.js +156 -2
- 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 +93 -19
- package/neural-interface/lib/assistant-plan-permissions.js +25 -9
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
- package/neural-interface/lib/assistant-route-gate.js +6 -4
- package/neural-interface/lib/assistant-router.js +245 -38
- package/neural-interface/lib/assistant-runtime.js +214 -30
- package/neural-interface/lib/assistant-task-prompt.js +87 -21
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +626 -4
- package/neural-interface/lib/backup-zip-reader.js +10 -5
- package/neural-interface/lib/backup-zip-writer.js +257 -0
- package/neural-interface/lib/browser-execution.js +10 -2
- package/neural-interface/lib/browser-guard-cdp.js +84 -0
- package/neural-interface/lib/browser-network-guard.js +476 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +198 -38
- package/neural-interface/lib/claude-agent-bridge.js +11 -2
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +163 -154
- package/neural-interface/lib/cli-resolver.js +13 -3
- package/neural-interface/lib/cli-update-planner.js +52 -41
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +191 -0
- package/neural-interface/lib/confined-fs.js +30 -3
- package/neural-interface/lib/crash-notice-api.js +98 -0
- package/neural-interface/lib/editor-completion-opencode.js +68 -68
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +13 -10
- 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/http-guards.js +8 -2
- package/neural-interface/lib/install-details.js +4 -1
- package/neural-interface/lib/mcp-agent-servers.js +67 -0
- package/neural-interface/lib/mcp-client-config.js +35 -33
- package/neural-interface/lib/mcp-external-servers.js +332 -0
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +177 -25
- package/neural-interface/lib/native-loop-providers.js +965 -66
- package/neural-interface/lib/native-loop-runtime.js +290 -42
- package/neural-interface/lib/opencode-config.js +557 -0
- package/neural-interface/lib/opencode-db-schema.js +17 -34
- package/neural-interface/lib/opencode-history.js +48 -218
- package/neural-interface/lib/opencode-mcp-ready.js +108 -0
- package/neural-interface/lib/opencode-native-boundary.js +53 -0
- package/neural-interface/lib/opencode-protocol.js +249 -112
- package/neural-interface/lib/opencode-runtime-config.js +32 -22
- package/neural-interface/lib/opencode-serve-records.js +39 -21
- package/neural-interface/lib/opencode-v2-client.js +48 -153
- package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
- package/neural-interface/lib/opencode-worker-data.js +27 -0
- package/neural-interface/lib/opencode2-client.js +73 -37
- package/neural-interface/lib/opencode2-proxy.js +23 -63
- package/neural-interface/lib/opencode2-translate.js +51 -2
- package/neural-interface/lib/owner-authority.js +249 -0
- package/neural-interface/lib/page-session.js +275 -0
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +793 -0
- 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 +20 -2
- package/neural-interface/lib/rulesets/api.js +7 -6
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +180 -36
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +81 -31
- package/neural-interface/lib/session-title-generator.js +58 -36
- package/neural-interface/lib/skills/api-v2.js +495 -0
- package/neural-interface/lib/skills/bundle.js +418 -0
- package/neural-interface/lib/skills/catalog.js +298 -0
- package/neural-interface/lib/skills/creation-api.js +191 -0
- package/neural-interface/lib/skills/creation-brief.js +247 -0
- package/neural-interface/lib/skills/creation-sources.js +285 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +569 -0
- package/neural-interface/lib/skills/digest.js +119 -0
- package/neural-interface/lib/skills/discover.js +111 -0
- package/neural-interface/lib/skills/draft-confinement.js +216 -0
- package/neural-interface/lib/skills/eval-confinement.js +399 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +135 -0
- package/neural-interface/lib/skills/evals-api.js +212 -0
- package/neural-interface/lib/skills/evals.js +1573 -0
- package/neural-interface/lib/skills/export.js +203 -0
- package/neural-interface/lib/skills/frontmatter.js +64 -0
- package/neural-interface/lib/skills/health.js +75 -0
- package/neural-interface/lib/skills/json-edit.js +165 -0
- package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
- package/neural-interface/lib/skills/known-bridges.js +96 -0
- package/neural-interface/lib/skills/library.js +823 -0
- package/neural-interface/lib/skills/local-store.js +21 -0
- package/neural-interface/lib/skills/migration.js +433 -0
- package/neural-interface/lib/skills/operations.js +45 -0
- package/neural-interface/lib/skills/ownership.js +427 -0
- package/neural-interface/lib/skills/packages.js +88 -0
- package/neural-interface/lib/skills/reviews.js +212 -0
- package/neural-interface/lib/skills/revision.js +80 -0
- package/neural-interface/lib/skills/roots.js +269 -0
- package/neural-interface/lib/skills/runtime-checks.js +358 -0
- package/neural-interface/lib/skills/scan-assets.js +252 -0
- package/neural-interface/lib/skills/scan-context.js +26 -0
- package/neural-interface/lib/skills/scan-shell.js +324 -0
- package/neural-interface/lib/skills/scan.js +364 -0
- package/neural-interface/lib/skills/scanners.js +54 -0
- package/neural-interface/lib/skills/service.js +530 -0
- package/neural-interface/lib/skills/telemetry.js +119 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/bridge.js +6 -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 +1268 -0
- package/neural-interface/lib/worker-guard-setting.js +56 -0
- package/neural-interface/lib/worker-mcp-channel.js +175 -0
- package/neural-interface/lib/worker-question.js +74 -0
- package/neural-interface/lib/worker-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +238 -0
- package/neural-interface/lib/worker-sandbox.js +329 -0
- package/neural-interface/package-lock.json +34 -12
- package/neural-interface/package.json +3 -2
- package/neural-interface/public/index.html +3 -1
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +3 -3
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +125 -31
- 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 +1527 -1336
- package/neural-interface/public/shared/app-frame.js +192 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
- package/neural-interface/public/shared/assistant/asst-api.js +1 -1
- package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
- package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
- package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-control.js +205 -26
- package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
- package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
- package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
- package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
- package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
- package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
- package/neural-interface/public/shared/assistant/asst-render.js +445 -82
- package/neural-interface/public/shared/assistant/asst-route.js +304 -53
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
- package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
- package/neural-interface/public/shared/assistant/asst-state.js +87 -24
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/automation-context.js +5 -2
- package/neural-interface/public/shared/cc-dropdown.js +89 -19
- package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
- package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
- package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
- package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
- package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
- package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
- package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +11 -12
- package/neural-interface/public/shared/cli-status.js +11 -10
- package/neural-interface/public/shared/constants.js +5 -4
- package/neural-interface/public/shared/context-menu-areas.js +34 -0
- package/neural-interface/public/shared/context-menu-logic.js +243 -0
- package/neural-interface/public/shared/context-menu-model.js +367 -0
- package/neural-interface/public/shared/context-menu.js +372 -0
- package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
- 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/crash-notice-view.js +168 -0
- 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 +21 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- 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 +30 -15
- package/neural-interface/public/shared/i18n.js +2 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
- package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
- package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
- package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
- package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
- package/neural-interface/public/shared/page-session.js +96 -0
- package/neural-interface/public/shared/provider-icons.js +4 -2
- package/neural-interface/public/shared/registry.js +5 -3
- package/neural-interface/public/shared/retired-2d.js +3 -0
- package/neural-interface/public/shared/schedules-view.js +195 -61
- package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
- package/neural-interface/public/shared/settings/settings-data.js +3 -1
- package/neural-interface/public/shared/settings/settings-ia.js +6 -6
- package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
- package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
- package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
- 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-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +222 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
- package/neural-interface/public/shared/skills/ss-doc.js +173 -0
- package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
- package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
- package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
- package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
- package/neural-interface/public/shared/skills/ss-evals.js +417 -0
- package/neural-interface/public/shared/skills/ss-form.js +159 -0
- package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
- package/neural-interface/public/shared/skills/ss-ids.js +32 -0
- package/neural-interface/public/shared/skills/ss-lint.js +149 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
- package/neural-interface/public/shared/skills/ss-resources.js +165 -0
- package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
- package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
- package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
- package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
- package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
- package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
- package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
- package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
- package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
- package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
- package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
- package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
- package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
- package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
- 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-geometry.js +82 -0
- package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
- package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
- package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
- package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
- package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
- package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
- package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
- package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
- package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
- package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
- package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
- package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +1822 -1756
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- package/neural-interface/public/shared/text-cache.js +29 -0
- package/neural-interface/public/shared/tool-list-picker.js +55 -0
- package/neural-interface/public/shared/tool-list-storage.js +24 -0
- package/neural-interface/public/shared/tool-list-view.js +112 -0
- package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
- package/neural-interface/public/shared/tour/tour-actor.js +268 -0
- package/neural-interface/public/shared/tour/tour-beats.js +336 -0
- package/neural-interface/public/shared/tour/tour-engine.js +884 -0
- package/neural-interface/public/shared/tour/tour-ink.js +390 -0
- package/neural-interface/public/shared/tour/tour-stage.js +440 -0
- package/neural-interface/public/shared/tour/tour-state.js +40 -0
- package/neural-interface/public/shared/tour/tour-tips.js +99 -0
- package/neural-interface/public/shared/tour/tour.css +144 -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 +111 -140
- package/neural-interface/public/shared/ui-claude-panel.js +376 -82
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- package/neural-interface/public/shared/ui-command-runner.js +6 -3
- package/neural-interface/public/shared/ui-context-menu.js +295 -0
- package/neural-interface/public/shared/ui-cost-widget.js +57 -59
- package/neural-interface/public/shared/ui-crash-notice.js +187 -0
- package/neural-interface/public/shared/ui-detail.js +15 -3
- 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-image-gallery.js +50 -47
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +33 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-menubar.js +10 -20
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
- package/neural-interface/public/shared/ui-navbar.js +49 -24
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +27 -15
- package/neural-interface/public/shared/ui-settings.js +683 -526
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
- package/neural-interface/public/shared/ui-skills.js +33 -2830
- package/neural-interface/public/shared/ui-styleguide.js +5 -2
- package/neural-interface/public/shared/ui-sync.js +15 -5
- package/neural-interface/public/shared/ui-terminal.js +270 -190
- package/neural-interface/public/shared/ui-tool-updates.js +6 -4
- package/neural-interface/public/shared/ui-tooltip.js +81 -51
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-trash.js +6 -4
- package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
- package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
- package/neural-interface/public/shared/ui-tutorial.css +47 -100
- package/neural-interface/public/shared/ui-tutorial.js +285 -509
- package/neural-interface/public/shared/ui-update.js +38 -2
- package/neural-interface/public/shared/ui-whiteboard.js +312 -59
- package/neural-interface/public/shared/ui-worker-end.js +5 -1
- package/neural-interface/public/shared/ui-workspaces.js +3 -2
- package/neural-interface/public/shared/view-variant.js +66 -0
- package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
- package/neural-interface/public/shared/whiteboard-list.js +172 -0
- package/neural-interface/public/shared/worker-end.js +10 -3
- package/neural-interface/public/shared/worker-questions.js +34 -0
- package/neural-interface/public/shared/worker-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +2 -2
- package/neural-interface/public/variant/2d/background.js +2 -1
- package/neural-interface/public/variant/2d/context-menu.js +8 -2
- package/neural-interface/public/variant/2d/graph.js +42 -20
- package/neural-interface/public/variant/2d/hulls.js +14 -9
- package/neural-interface/public/variant/2d/lasso.js +11 -7
- package/neural-interface/public/variant/2d/main.js +32 -1
- package/neural-interface/public/variant/2d/minimap.js +11 -13
- package/neural-interface/public/variant/3d/main.js +69 -6
- package/neural-interface/public/variant/3d/map-controls.js +24 -9
- package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat.css +17 -0
- package/neural-interface/public/vendor/caveat/manifest.json +12 -0
- package/neural-interface/public/vendor/yaml/LICENSE +13 -0
- package/neural-interface/public/vendor/yaml/manifest.json +4 -0
- package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
- package/neural-interface/scripts/build-vendor.mjs +32 -0
- package/neural-interface/server.js +4553 -2855
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +34 -12
- package/package.json +17 -3
- package/postinstall.js +9 -4
- package/preuninstall.js +1 -1
- package/setup.js +97 -322
- package/skills/skill-forge/SKILL.md +177 -0
- package/skills/skill-forge/evals/evals.json +54 -0
- package/skills/skill-forge/references/package.md +105 -0
- package/skills/skill-forge/references/review-and-recovery.md +63 -0
- package/skills/skill-forge/references/runtime-profiles.md +20 -0
- package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
- package/skills/synabun/SKILL.md +10 -7
- package/skills/synabun/modules/audit.md +1 -5
- package/skills/synabun/modules/changelog.md +1 -5
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/memorize.md +6 -9
- package/skills/synabun/modules/schedule.md +6 -6
- package/mcp-server/dist/services/profiles.d.ts +0 -120
- package/mcp-server/dist/services/profiles.d.ts.map +0 -1
- package/mcp-server/dist/services/profiles.js +0 -387
- package/mcp-server/dist/services/profiles.js.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
- package/mcp-server/dist/tools/leonardo.d.ts +0 -13
- package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo.js +0 -21
- package/mcp-server/dist/tools/leonardo.js.map +0 -1
- package/mcp-server/dist/tools/profile.d.ts +0 -17
- package/mcp-server/dist/tools/profile.d.ts.map +0 -1
- package/mcp-server/dist/tools/profile.js +0 -114
- package/mcp-server/dist/tools/profile.js.map +0 -1
- package/mcp-server/src/services/profiles.ts +0 -428
- package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
- package/mcp-server/src/tools/leonardo.ts +0 -30
- package/mcp-server/src/tools/profile.ts +0 -122
- package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
- package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
- package/neural-interface/lib/mcp-profile-servers.js +0 -120
- package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
- package/skills/leonardo/SKILL.md +0 -168
- package/skills/leonardo/icon.svg +0 -1
- package/skills/leonardo/modules/browser-guide.md +0 -378
- package/skills/leonardo/modules/image-prompter.md +0 -283
- package/skills/leonardo/modules/model-advisor.md +0 -100
- package/skills/leonardo/modules/prompt-library.md +0 -110
- package/skills/leonardo/modules/style-guide.md +0 -186
- package/skills/leonardo/modules/video-prompter.md +0 -317
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { fetchToolCatalog } from './agent-runtime-options.js';
|
|
2
|
+
import { configureToolPickerTranslation, toolPickerHTML, readToolPicker, toolWord } from './tool-list-picker.js';
|
|
1
3
|
import { mountMemoryMaintenance } from './ui-memory-maintenance.js';
|
|
2
4
|
import { buildJudgmentsTab, wireJudgmentsTab } from './ui-judgments.js';
|
|
3
5
|
import { buildWhatsAppTab, wireWhatsAppTab } from './ui-whatsapp.js';
|
|
@@ -6,14 +8,19 @@ import { buildAboutTab, wireAboutTab } from './settings/settings-about-pane.js';
|
|
|
6
8
|
import { buildAppTab, wireAppTab } from './settings/settings-app-pane.js';
|
|
7
9
|
import { SETTINGS_GROUPS, SETTINGS_PAGES, DEFAULT_SETTINGS_PAGE, pageKeys, getSettingsPage, resolveSettingsTarget, homeOfControl } from './settings/settings-ia.js';
|
|
8
10
|
import * as kit from './settings/settings-kit.js';
|
|
11
|
+
import { skillFoldersHTML, wireSkillFolders, refreshSkillChips, chipState, installedToast } from './settings/settings-skill-folders-pane.js';
|
|
9
12
|
import { RULES_HOST_LABELS, describeRulesHost, describeRulesInstall, describeRulesInstallAll } from './settings/settings-rules.js';
|
|
10
13
|
import { readUserContextResponse, describeUserContext, describeUserContextSaveFailure } from './settings/settings-user-context.js';
|
|
14
|
+
import { readWorkerGuardResponse, describeWorkerGuard, describeWorkerGuardSaveFailure } from './settings/settings-worker-guard.js';
|
|
15
|
+
import { CLAUDE_HOOK_ROWS, readClaudeHooksResponse, describeClaudeHooks, describeClaudeHooksSaveFailure } from './settings/settings-claude-hooks.js';
|
|
11
16
|
import { readGreetingStatus, describeGreeting, describeGreetingSaveFailure } from './settings/settings-greeting.js';
|
|
17
|
+
import { describeOpencodeInstall } from './settings/settings-opencode.js';
|
|
12
18
|
import { renderGreeting, clearGreeting } from './greeting/greeting-card.js';
|
|
13
19
|
import { createTunnelCard, drawInto, mcpKeyNeededText, readTunnel } from './ui-tunnel.js';
|
|
14
20
|
import { remoteMcpAddress } from './tunnel-view.js';
|
|
15
21
|
import { recheckCliStatus, getCliStatus, addCliToPath } from './cli-status.js';
|
|
16
22
|
import { cliInstallText, setCliInstallTranslator } from './cli-install-view.js';
|
|
23
|
+
import { dropdownHTML, wireDropdowns, setDropdownItems, setDropdownValue } from './cc-dropdown.js';
|
|
17
24
|
setCliInstallTranslator((key, params) => kit.tx(`cliInstall.${key}`, params));
|
|
18
25
|
import { setGitBashTranslator } from './git-bash-view.js';
|
|
19
26
|
import { gitBashRowHtml, wireGitBashRow } from './ui-git-bash.js';
|
|
@@ -37,6 +44,7 @@ import { state, emit, on } from './state.js';
|
|
|
37
44
|
import { getSettingsTabs } from './registry.js';
|
|
38
45
|
import { escapeHtml } from './utils.js';
|
|
39
46
|
import { storage } from './storage.js';
|
|
47
|
+
import { normalizeViewConfig } from './view-variant.js';
|
|
40
48
|
import { KEYS } from './constants.js';
|
|
41
49
|
import { registerAction } from './ui-keybinds.js';
|
|
42
50
|
import { CLI_PROFILES, ensureModelsForProfile, getModelsForProfile } from './agent-runtime-options.js';
|
|
@@ -47,6 +55,7 @@ import { getNotifSettings, playTestSound, sendTestBanner, SOUND_PRESETS } from '
|
|
|
47
55
|
import {
|
|
48
56
|
fetchRulesStatus, fetchRulesTextWithFallback, installRules, removeRules, installAllRules, setRulesAutoUpdate, removeLegacyRules,
|
|
49
57
|
} from './api.js';
|
|
58
|
+
import { centeredInFrame } from './app-frame.js';
|
|
50
59
|
|
|
51
60
|
// ── SVG icon constants ──
|
|
52
61
|
|
|
@@ -89,7 +98,8 @@ const TAB_ICONS = {
|
|
|
89
98
|
const COPY_ICON = '<svg viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
|
|
90
99
|
const CHEVRON_ICON = '<svg class="cc-section-chevron" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>';
|
|
91
100
|
const ANTHROPIC_ICON = '<svg viewBox="0 0 24 24" class="cc-provider-icon"><path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z" fill="currentColor"/></svg>';
|
|
92
|
-
|
|
101
|
+
// A line drawing: its paths carry fill="none" themselves, because `.cli-path-icon svg` fills every icon it holds.
|
|
102
|
+
const ANTIGRAVITY_ICON = '<svg viewBox="0 0 24 24" class="cc-provider-icon" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 20 12 4l7.5 16" fill="none"/><path d="M8.2 20a3.8 3.8 0 0 1 7.6 0" fill="none"/></svg>';
|
|
93
103
|
const OPENAI_ICON = '<svg viewBox="0 0 24 24" class="cc-provider-icon"><path d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z" fill="currentColor"/></svg>';
|
|
94
104
|
const OPENCODE_ICON = '<svg viewBox="0 0 24 30" class="cc-provider-icon" fill="currentColor"><path d="M18 6H6V24H18V6ZM24 30H0V0H24V30Z"/></svg>';
|
|
95
105
|
const BROWSER_ICON = '<svg viewBox="0 0 24 24" class="cc-provider-icon"><circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.5"/><ellipse cx="12" cy="12" rx="4" ry="10" fill="none" stroke="currentColor" stroke-width="1.5"/><line x1="2" y1="12" x2="22" y2="12" stroke="currentColor" stroke-width="1.5"/><path d="M4.5 7h15M4.5 17h15" fill="none" stroke="currentColor" stroke-width="1"/></svg>';
|
|
@@ -113,6 +123,7 @@ function showCCToast(msg, duration = 3500) {
|
|
|
113
123
|
// INTERFACE CUSTOMIZATION — config, presets, apply
|
|
114
124
|
// ═══════════════════════════════════════════
|
|
115
125
|
|
|
126
|
+
// Optional legacy view preferences must never reintroduce a 2D selection.
|
|
116
127
|
const IFACE_DEFAULTS = {
|
|
117
128
|
visualizationEnabled: true,
|
|
118
129
|
scale: 1,
|
|
@@ -161,7 +172,7 @@ const IFACE_SLIDERS = {
|
|
|
161
172
|
export function loadIfaceConfig() {
|
|
162
173
|
try {
|
|
163
174
|
const raw = storage.getItem('neural-interface-config');
|
|
164
|
-
if (raw) return { ...IFACE_DEFAULTS, ...JSON.parse(raw) };
|
|
175
|
+
if (raw) return { ...IFACE_DEFAULTS, ...normalizeViewConfig(JSON.parse(raw)) };
|
|
165
176
|
// Migrate legacy ui-scale if present
|
|
166
177
|
const legacyScale = storage.getItem('neural-ui-scale');
|
|
167
178
|
if (legacyScale) {
|
|
@@ -174,7 +185,7 @@ export function loadIfaceConfig() {
|
|
|
174
185
|
}
|
|
175
186
|
|
|
176
187
|
export function saveIfaceConfig(cfg) {
|
|
177
|
-
storage.setItem('neural-interface-config', JSON.stringify(cfg));
|
|
188
|
+
storage.setItem('neural-interface-config', JSON.stringify(normalizeViewConfig(cfg)));
|
|
178
189
|
// Keep legacy key in sync for backwards compat
|
|
179
190
|
storage.setItem('neural-ui-scale', cfg.scale);
|
|
180
191
|
}
|
|
@@ -521,21 +532,29 @@ function buildNavHTML(activeId) {
|
|
|
521
532
|
return html;
|
|
522
533
|
}
|
|
523
534
|
|
|
524
|
-
/** The nav rail's stand-in on a narrow panel: one labelled
|
|
535
|
+
/** The nav rail's stand-in on a narrow panel: one labelled dropdown that lists every page. */
|
|
525
536
|
function buildPagePickerHTML(activeId) {
|
|
526
537
|
const groups = SETTINGS_GROUPS.map(group => {
|
|
527
538
|
const options = SETTINGS_PAGES.filter(p => p.group === group.id).map(page => {
|
|
528
539
|
const keys = pageKeys(page);
|
|
529
|
-
return
|
|
530
|
-
})
|
|
531
|
-
return options ?
|
|
532
|
-
}).
|
|
540
|
+
return { value: page.id, label: `${kit.tx(keys.navLabel)} — ${kit.tx(keys.navHelp)}`, selected: page.id === activeId };
|
|
541
|
+
});
|
|
542
|
+
return options.length ? { group: kit.tx(group.labelKey), options } : null;
|
|
543
|
+
}).filter(Boolean);
|
|
544
|
+
// dropdownHTML escapes the labels itself.
|
|
533
545
|
return `<div class="stg-page-picker">
|
|
534
|
-
<label for="stg-page-select">${kit.te('settings.redesign.common.allPages')}</label>
|
|
535
|
-
|
|
546
|
+
<label id="stg-page-select-label" for="stg-page-select-trigger">${kit.te('settings.redesign.common.allPages')}</label>
|
|
547
|
+
${dropdownHTML('stg-page-select', groups, { classes: 'stg-page-dropdown', labelledBy: 'stg-page-select-label' })}
|
|
536
548
|
</div>`;
|
|
537
549
|
}
|
|
538
550
|
|
|
551
|
+
/** A settings field whose control is a styled dropdown: the id stays on its hidden input, the label and help point at its button. */
|
|
552
|
+
function dropdownField(invId, id, entries, { classes = '' } = {}) {
|
|
553
|
+
const control = dropdownHTML(id, entries, { classes, labelledBy: `${id}-label` })
|
|
554
|
+
.replace('role="combobox"', `role="combobox" aria-describedby="${id}-help"`);
|
|
555
|
+
return kit.field(invId, control, { forId: id }).replace(`<label for="${id}">`, `<label id="${id}-label" for="${id}-trigger">`);
|
|
556
|
+
}
|
|
557
|
+
|
|
539
558
|
/** The search field above the page list, and the results list that takes the list's place while it holds a query. */
|
|
540
559
|
function buildSearchHTML() {
|
|
541
560
|
return `<div class="stg-search" role="search">
|
|
@@ -551,7 +570,7 @@ function buildSearchHTML() {
|
|
|
551
570
|
}
|
|
552
571
|
|
|
553
572
|
// What search reads from the panel beside its index (settings-search.js): the labelled rows of the panes that draw
|
|
554
|
-
// themselves, and the names of what the panes load (projects, providers, tools, tool
|
|
573
|
+
// themselves, and the names of what the panes load (projects, providers, tools, tool lists and servers, skills,
|
|
555
574
|
// browser profiles, themes, the linked WhatsApp account). Labels and names only: a source names label elements and
|
|
556
575
|
// the shell reads their text, never a value; `help` is given only where it is the app's own copy. `named` rows may
|
|
557
576
|
// sit inside an inventory control (a list it tags).
|
|
@@ -805,9 +824,9 @@ function buildBrowserTab() {
|
|
|
805
824
|
${kit.field('BRW011', dd('BRW011', 'bc-connectMode', 'connect', ['mirror', 'attach', 'morelogin']))}
|
|
806
825
|
${kit.field('BRW012', dd('BRW012', 'bc-attachSeed', 'seed', ['real', 'fresh']), { attrs: 'id="bc-attachSeed-row" style="display:none"' })}
|
|
807
826
|
<div class="stg-field bc-card-row" id="bc-morelogin-row" ${kit.inv('BRW013')} style="display:none">
|
|
808
|
-
<label for="bc-morelogin-env-id">${kit.L('BRW013')}</label>
|
|
827
|
+
<label id="bc-morelogin-env-id-label" for="bc-morelogin-env-id-trigger">${kit.L('BRW013')}</label>
|
|
809
828
|
<div class="browser-cfg-input-row">
|
|
810
|
-
|
|
829
|
+
${dropdownHTML('bc-morelogin-env-id', [{ value: '', label: kit.tx('settings.redesign.browser.loadingProfiles') }], { classes: 'bc-ml-dropdown', labelledBy: 'bc-morelogin-env-id-label', attrs: 'style="flex:1;min-width:0"' }).replace('role="combobox"', 'role="combobox" aria-describedby="bc-morelogin-env-id-help"')}
|
|
811
830
|
<button type="button" class="cli-detect-btn" id="bc-morelogin-refresh" ${kit.inv('BRW014')} title="${kit.H('BRW014')}">${REFRESH}${kit.L('BRW014')}</button>
|
|
812
831
|
</div>
|
|
813
832
|
<div class="stg-help" id="bc-morelogin-env-id-help">${kit.H('BRW013')}</div>
|
|
@@ -955,9 +974,9 @@ function buildSetupTab(setupStatus) {
|
|
|
955
974
|
// primary,remove,view,copy,text}, and the header badge (#setup-<id> .gfx-group-title .setup-status-badge).
|
|
956
975
|
const providers = [
|
|
957
976
|
{ id: 'claude', name: 'Claude', icon: ANTHROPIC_ICON, file: '~/.claude.json', ids: { toggle: 'SET004', cli: 'SET005', primary: 'SET006', remove: 'SET007', view: 'SET008', copy: 'SET009' } },
|
|
958
|
-
{ id: '
|
|
977
|
+
{ id: 'antigravity', name: 'Antigravity', icon: ANTIGRAVITY_ICON, file: '~/.gemini/config/mcp_config.json', ids: { toggle: 'SET010', config: 'SET011', primary: 'SET012', remove: 'SET013', view: 'SET014', copy: 'SET015' } },
|
|
959
978
|
{ id: 'codex', name: 'Codex', icon: OPENAI_ICON, file: '~/.codex/config.toml', ids: { toggle: 'SET016', cli: 'SET017', config: 'SET018', primary: 'SET019', remove: 'SET020', view: 'SET021', copy: 'SET022' } },
|
|
960
|
-
{ id: 'opencode', name: 'OpenCode', icon: OPENCODE_ICON, file: '~/.config/opencode/
|
|
979
|
+
{ id: 'opencode', name: 'OpenCode', icon: OPENCODE_ICON, file: '~/.config/opencode/opencode.json', ids: { toggle: 'SET023', config: 'SET024', primary: 'SET025', remove: 'SET026', view: 'SET027', copy: 'SET028' } },
|
|
961
980
|
].map((p) => ({ ...p, on: setupStatus[p.id]?.connected || false }));
|
|
962
981
|
|
|
963
982
|
const copyLabel = (invId) => `${COPY_ICON} ${kit.L(invId)}`;
|
|
@@ -1095,7 +1114,7 @@ function buildTerminalTab(cliConfig) {
|
|
|
1095
1114
|
const cliProfiles = [
|
|
1096
1115
|
{ id: 'claude-code', name: 'Claude Code', icon: ANTHROPIC_ICON, color: '#D4A27F', default: 'claude', inv: 'CLI001' },
|
|
1097
1116
|
{ id: 'codex', name: 'Codex', icon: OPENAI_ICON, color: '#74c7a5', default: 'codex', inv: 'CLI003' },
|
|
1098
|
-
{ id: '
|
|
1117
|
+
{ id: 'antigravity', name: 'Antigravity', icon: ANTIGRAVITY_ICON, color: '#669DF6', default: 'agy', inv: 'CLI004' },
|
|
1099
1118
|
{ id: 'opencode', name: 'OpenCode', icon: OPENCODE_ICON, color: '#E8E0DC', default: 'opencode', inv: 'CLI005' },
|
|
1100
1119
|
];
|
|
1101
1120
|
|
|
@@ -1118,7 +1137,7 @@ function buildTerminalTab(cliConfig) {
|
|
|
1118
1137
|
<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
|
1119
1138
|
${kit.L('CLI002')}
|
|
1120
1139
|
</button>
|
|
1121
|
-
|
|
1140
|
+
<button type="button" class="cli-detect-btn" data-cli-add-path="${p.id}" hidden>${escapeHtml(cliInstallText('addPath'))}</button>
|
|
1122
1141
|
<span class="cli-path-status${isDefault ? '' : ' custom'}" id="cli-status-${p.id}" ${kit.inv('CLI007')}>${kit.te(isDefault ? 'settings.redesign.terminal.cliStatus.default' : 'settings.redesign.terminal.cliStatus.custom')}</span>
|
|
1123
1142
|
</div>
|
|
1124
1143
|
<div class="stg-help" id="cli-path-${p.id}-help">${kit.H(p.inv)}</div>
|
|
@@ -1131,7 +1150,7 @@ function buildTerminalTab(cliConfig) {
|
|
|
1131
1150
|
{ code: 'C:\\Users\\me\\.npm\\claude', key: 'exWindows' },
|
|
1132
1151
|
{ code: '/opt/homebrew/bin/claude', key: 'exUnix' },
|
|
1133
1152
|
{ code: 'wsl claude', key: 'exWsl' },
|
|
1134
|
-
{ code: 'wsl -d Ubuntu
|
|
1153
|
+
{ code: 'wsl -d Ubuntu claude', key: 'exWslDistro' },
|
|
1135
1154
|
];
|
|
1136
1155
|
|
|
1137
1156
|
return `
|
|
@@ -1175,23 +1194,23 @@ function buildMcpTab() {
|
|
|
1175
1194
|
return `
|
|
1176
1195
|
<div class="stg-pane" data-stg-pane="mcp">
|
|
1177
1196
|
<div class="stg-mcp-page-tabs" role="tablist" aria-label="${kit.L('MCP001')}" ${kit.inv('MCP001')}>
|
|
1178
|
-
${tab('stg-mcp-page-tab', 'data-pane', '
|
|
1197
|
+
${tab('stg-mcp-page-tab', 'data-pane', 'tools', m('tab.tools'), true)}
|
|
1179
1198
|
${tab('stg-mcp-page-tab', 'data-pane', 'installed', m('tab.installed'), false)}
|
|
1180
1199
|
</div>
|
|
1181
1200
|
|
|
1182
|
-
<div class="stg-mcp-pane" data-pane="
|
|
1201
|
+
<div class="stg-mcp-pane" data-pane="tools" data-stg-reveal='.stg-mcp-page-tab[data-pane="tools"]'>
|
|
1183
1202
|
${kit.card({
|
|
1184
|
-
id: 'stg-sec-
|
|
1203
|
+
id: 'stg-sec-default-tools', section: 'connected_tools.default_tools',
|
|
1185
1204
|
body: `
|
|
1186
1205
|
<div class="stg-mcp-intro">
|
|
1187
|
-
<div class="stg-help" style="margin-top:0">${m('
|
|
1206
|
+
<div class="stg-help" style="margin-top:0">${m('defaultToolsHelp')}</div>
|
|
1188
1207
|
<div class="stg-mcp-note">
|
|
1189
1208
|
<strong>${m('noteTitle')}</strong>
|
|
1190
1209
|
<span>${m('noteBody')}</span>
|
|
1191
1210
|
</div>
|
|
1192
1211
|
</div>
|
|
1193
|
-
<div id="stg-mcp-
|
|
1194
|
-
<div class="stg-mcp-
|
|
1212
|
+
<div id="stg-mcp-default-tools" class="stg-mcp-default-tools stg-mcp-block" ${kit.inv('MCP022')} role="group" aria-label="${kit.L('MCP022')}">
|
|
1213
|
+
<div class="stg-mcp-empty-note">${m('loading')}</div>
|
|
1195
1214
|
</div>
|
|
1196
1215
|
<div id="stg-mcp-always-on" class="stg-mcp-subsection" ${kit.inv('MCP024')}></div>
|
|
1197
1216
|
<div class="stg-mcp-subsection stg-mcp-servers-section">
|
|
@@ -1241,12 +1260,7 @@ function buildMcpTab() {
|
|
|
1241
1260
|
</div>
|
|
1242
1261
|
<div class="stg-mcp-form-grid" style="margin-top:10px">
|
|
1243
1262
|
${kit.field('MCP012', input('stg-mcp-srv-name', { ph: m('ph.name') }), { forId: 'stg-mcp-srv-name' })}
|
|
1244
|
-
${
|
|
1245
|
-
<select class="stg-input stg-input-sm" id="stg-mcp-srv-type" aria-describedby="stg-mcp-srv-type-help">
|
|
1246
|
-
<option value="stdio">${m('type.stdio')}</option>
|
|
1247
|
-
<option value="sse">${m('type.sse')}</option>
|
|
1248
|
-
<option value="http">${m('type.http')}</option>
|
|
1249
|
-
</select>`, { forId: 'stg-mcp-srv-type' })}
|
|
1263
|
+
${dropdownField('MCP013', 'stg-mcp-srv-type', ['stdio', 'sse', 'http'].map(t => ({ value: t, label: kit.tx(`settings.redesign.mcp.type.${t}`), selected: t === 'stdio' })))}
|
|
1250
1264
|
${kit.field('MCP014', input('stg-mcp-srv-cmd', { ph: 'node' }), { forId: 'stg-mcp-srv-cmd', className: 'stg-mcp-field-wide stg-mcp-field-stdio', attrs: 'data-stg-shown-by="#stg-mcp-srv-type"' })}
|
|
1251
1265
|
${kit.field('MCP015', `
|
|
1252
1266
|
<div class="stg-mcp-arg-chips" id="stg-mcp-arg-chips" ${kit.inv('MCP027')}>
|
|
@@ -1272,7 +1286,7 @@ function buildMcpTab() {
|
|
|
1272
1286
|
${platform('claudeCode', true)}
|
|
1273
1287
|
${platform('opencode', true)}
|
|
1274
1288
|
${platform('codex', true)}
|
|
1275
|
-
${platform('
|
|
1289
|
+
${platform('antigravity', false)}
|
|
1276
1290
|
</div>
|
|
1277
1291
|
<div class="stg-action-row stg-mcp-form-actions" style="margin-top:10px">
|
|
1278
1292
|
<button type="button" class="stg-action-btn compact" id="stg-mcp-srv-save" ${kit.inv('MCP020')} title="${kit.H('MCP020')}">${kit.L('MCP020')}</button>
|
|
@@ -1303,6 +1317,7 @@ function buildOpencodeTab() {
|
|
|
1303
1317
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 2v6h-6"/><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M3 22v-6h6"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/></svg>
|
|
1304
1318
|
</button>
|
|
1305
1319
|
</span>`, { forId: 'stg-ocp-port', className: 'ocp-server-bar' })}
|
|
1320
|
+
<div class="stg-help ocp-install-note" id="stg-ocp-install" role="status" aria-live="polite" hidden></div>
|
|
1306
1321
|
</div>
|
|
1307
1322
|
|
|
1308
1323
|
<div class="ocp-group-head">
|
|
@@ -1328,9 +1343,10 @@ function buildOpencodeTab() {
|
|
|
1328
1343
|
<div class="ocp-group">
|
|
1329
1344
|
${kit.advanced(`
|
|
1330
1345
|
<div class="ocp-config-footer" ${kit.inv('OCP019')}>
|
|
1331
|
-
<span class="ocp-config-path">${kit.th('settings.redesign.opencode.configPath', { path: '<code class="stg-code">~/.config/opencode/
|
|
1346
|
+
<span class="ocp-config-path">${kit.th('settings.redesign.opencode.configPath', { path: '<code class="stg-code" id="stg-ocp-config-path">~/.config/opencode/opencode.json</code>' })}</span>
|
|
1332
1347
|
<button type="button" class="ocp-config-link" id="stg-ocp-open-config" aria-describedby="stg-ocp-open-config-help">${kit.L('OCP019')}</button>
|
|
1333
1348
|
</div>
|
|
1349
|
+
<p class="stg-help" id="stg-ocp-config-jsonc" hidden></p>
|
|
1334
1350
|
${kit.help('OCP019', { forId: 'stg-ocp-open-config' })}`)}
|
|
1335
1351
|
</div>`,
|
|
1336
1352
|
})}
|
|
@@ -1341,8 +1357,7 @@ function buildOpencodeTab() {
|
|
|
1341
1357
|
body: `
|
|
1342
1358
|
<div class="ocp-compact-group ocp-rows">
|
|
1343
1359
|
${check('OCP013', 'stg-ocp-compact-auto')}
|
|
1344
|
-
${
|
|
1345
|
-
${kit.field('OCP015', `<input type="number" class="ocp-reserved-input" id="stg-ocp-compact-reserved" value="10000" min="1000" max="50000" step="1000" aria-describedby="stg-ocp-compact-reserved-help">`, { forId: 'stg-ocp-compact-reserved' })}
|
|
1360
|
+
${kit.field('OCP015', `<input type="number" class="ocp-reserved-input" id="stg-ocp-compact-reserved" value="" placeholder="${kit.te('settings.redesign.opencode.bufferDefault')}" min="0" max="1000000" step="1000" aria-describedby="stg-ocp-compact-reserved-help">`, { forId: 'stg-ocp-compact-reserved' })}
|
|
1346
1361
|
${kit.toggleField('OCP016', `<button type="button" class="cc-toggle on" id="stg-ocp-snapshot-toggle" aria-labelledby="stg-ocp-snapshot-toggle-label" aria-describedby="stg-ocp-snapshot-toggle-help"></button>`, { forId: 'stg-ocp-snapshot-toggle', className: 'ocp-snapshot-row' })}
|
|
1347
1362
|
</div>
|
|
1348
1363
|
<p class="ocp-footnote">${kit.te('settings.redesign.opencode.snapshotDetail')}</p>`,
|
|
@@ -1602,11 +1617,10 @@ function buildYoutubeTab(yt) {
|
|
|
1602
1617
|
${key('YT008', 'STEAM_API_KEY', 'xxxx', 'password')}
|
|
1603
1618
|
|
|
1604
1619
|
${kit.subhead(y('pipelineTitle'))}
|
|
1605
|
-
${
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
</select>`, { forId: 'yt-upload-transport' })}
|
|
1620
|
+
${dropdownField('YT009', 'yt-upload-transport', [
|
|
1621
|
+
{ value: 'browser', label: kit.tx('settings.redesign.youtube.transport.browser'), selected: p.uploadTransport !== 'api' },
|
|
1622
|
+
{ value: 'api', label: kit.tx('settings.redesign.youtube.transport.api'), selected: p.uploadTransport === 'api' },
|
|
1623
|
+
])}
|
|
1610
1624
|
${kit.field('YT010', `
|
|
1611
1625
|
<div class="settings-key-row">
|
|
1612
1626
|
<input id="yt-quality" type="text" value="${esc(dl.quality || 'bestvideo[height<=1080]+bestaudio/best[height<=1080]')}" autocomplete="off" spellcheck="false" aria-describedby="yt-quality-help">
|
|
@@ -1660,22 +1674,31 @@ function buildSocialTab(toolCategories, toolPermissions) {
|
|
|
1660
1674
|
|
|
1661
1675
|
function buildSkillsTab(ccSkills) {
|
|
1662
1676
|
const skills = ccSkills || [];
|
|
1677
|
+
// Antigravity reads ~/.gemini/config/skills: its chip shows once the server lists that target for the skills (an
|
|
1678
|
+
// older server installs to Claude Code, Codex and OpenCode only, and would refuse the chip's request).
|
|
1679
|
+
const antigravity = skills.some(s => s?.targetStates?.antigravity || (s?.installedTargets && Object.hasOwn(s.installedTargets, 'antigravity')));
|
|
1663
1680
|
const targets = [
|
|
1664
1681
|
{ id: 'claude', label: kit.te('settings.redesign.product.claude') },
|
|
1665
1682
|
{ id: 'codex', label: kit.te('settings.redesign.product.codex') },
|
|
1666
1683
|
{ id: 'opencode', label: kit.te('settings.redesign.product.opencode') },
|
|
1684
|
+
...(antigravity ? [{ id: 'antigravity', label: kit.te('settings.redesign.product.antigravity') }] : []),
|
|
1667
1685
|
];
|
|
1668
1686
|
const row = (skill) => {
|
|
1669
|
-
const installed = skill.installedTargets || { claude: !!skill.installed, codex: false, opencode: false };
|
|
1687
|
+
const installed = skill.installedTargets || { claude: !!skill.installed, codex: false, opencode: false, antigravity: false };
|
|
1670
1688
|
const anyOn = targets.some(t => installed[t.id]);
|
|
1671
|
-
|
|
1672
|
-
|
|
1689
|
+
// What each runtime has of it: a native skill folder, an old bridge not moved yet, or a folder of that name
|
|
1690
|
+
// SynaBun did not install (settings/settings-skill-folders.js chipState).
|
|
1691
|
+
const chips = targets.map(t => {
|
|
1692
|
+
const chip = chipState(skill.targetStates?.[t.id], installed[t.id]);
|
|
1693
|
+
return `
|
|
1694
|
+
<button type="button" class="cc-skill-target${chip.on ? ' on' : ''}"
|
|
1673
1695
|
data-cc-skill="${kit.esc(skill.dirName)}"
|
|
1674
1696
|
data-cc-target="${t.id}"
|
|
1675
|
-
data-state="${
|
|
1676
|
-
${kit.inv('SKL001')} data-stg-pressed="on" aria-pressed="${
|
|
1677
|
-
title="${kit.te(
|
|
1678
|
-
|
|
1697
|
+
data-state="${chip.on ? 'on' : 'off'}" data-install-state="${chip.state}"
|
|
1698
|
+
${kit.inv('SKL001')} data-stg-pressed="on" aria-pressed="${chip.on ? 'true' : 'false'}"
|
|
1699
|
+
title="${kit.te(chip.key, { skill: skill.name, assistant: t.label })}">${t.label}</button>
|
|
1700
|
+
`;
|
|
1701
|
+
}).join('');
|
|
1679
1702
|
return `
|
|
1680
1703
|
<div class="cc-skill-row${anyOn ? ' installed' : ''}" data-skill-name="${kit.esc(skill.dirName)}">
|
|
1681
1704
|
<div class="cc-skill-info">
|
|
@@ -1690,10 +1713,14 @@ function buildSkillsTab(ccSkills) {
|
|
|
1690
1713
|
<div class="stg-pane" data-stg-pane="skills">
|
|
1691
1714
|
${kit.card({
|
|
1692
1715
|
id: 'stg-sec-skills', section: 'tool_access.skills',
|
|
1693
|
-
body: skills.length
|
|
1716
|
+
body: (skills.length
|
|
1694
1717
|
? `<div ${kit.inv('SKL004')} role="group" aria-label="${kit.L('SKL004')}">${skills.map(row).join('')}</div>
|
|
1695
1718
|
${kit.help('SKL001')}`
|
|
1696
|
-
: `<div class="stg-help" style="margin-top:0">${kit.te('settings.redesign.skills.none')}</div
|
|
1719
|
+
: `<div class="stg-help" style="margin-top:0">${kit.te('settings.redesign.skills.none')}</div>`)
|
|
1720
|
+
// Skills Studio is where a skill is edited, versioned and published; these chips stay for what SynaBun ships.
|
|
1721
|
+
+ `<div class="cc-skill-studio"><button type="button" class="stg-action-btn compact" data-cc-open-studio="1">${kit.te('settings.redesign.skills.openStudio')}</button>
|
|
1722
|
+
<span class="stg-help">${kit.te('settings.redesign.skills.openStudioHelp')}</span></div>`
|
|
1723
|
+
+ skillFoldersHTML(),
|
|
1697
1724
|
})}
|
|
1698
1725
|
</div>`;
|
|
1699
1726
|
}
|
|
@@ -1704,7 +1731,7 @@ function buildPermissionsTab(toolCategories, toolPermissions) {
|
|
|
1704
1731
|
// The inventory ids of the "all tools of this group" switches, by group.
|
|
1705
1732
|
const ALL_SWITCH = {
|
|
1706
1733
|
memory: 'PER001', browser: 'PER003', whiteboard: 'PER004', cards: 'PER005', automation: 'PER006',
|
|
1707
|
-
|
|
1734
|
+
images: 'PER008', gsc: 'PER009', youtube: 'PER010', thirdparty: 'PER011',
|
|
1708
1735
|
};
|
|
1709
1736
|
|
|
1710
1737
|
let totalOn = 0, totalAll = 0;
|
|
@@ -1824,6 +1851,52 @@ const userContextDigestHTML = (digest) => (digest.rows.length
|
|
|
1824
1851
|
? digest.rows.map(row => `<button type="button" class="uc-digest-row" role="listitem" data-uc-memory="${kit.esc(row.id)}" title="${kit.te('settings.redesign.hooks.uc.openMemory')}"><span class="uc-digest-statement">${kit.esc(row.statement)}</span><span class="uc-digest-facet">${kit.esc(row.facet)}${row.seed ? ` · ${kit.te('settings.redesign.hooks.uc.seed')}` : ''}</span></button>`).join('')
|
|
1825
1852
|
: `<div class="stg-help uc-digest-message" role="listitem" style="margin:0">${kit.esc(digest.message)}</div>`);
|
|
1826
1853
|
|
|
1854
|
+
/**
|
|
1855
|
+
* Put a view (describeClaudeHooks) on the section: the line above, the switch, its notes and Repair, and the
|
|
1856
|
+
* per-moment rows and their count in the Hooks card, which show nothing on or off that was not read.
|
|
1857
|
+
*/
|
|
1858
|
+
function drawClaudeHooks(overlay, view) {
|
|
1859
|
+
const section = overlay.querySelector('#stg-sec-automation-behavior');
|
|
1860
|
+
if (!section) return;
|
|
1861
|
+
const note = section.querySelector('#ch-status-note');
|
|
1862
|
+
if (note) { note.hidden = !view.note; note.textContent = view.note?.text || ''; note.dataset.chState = view.state; }
|
|
1863
|
+
const rowBox = section.querySelector('#ch-row');
|
|
1864
|
+
if (rowBox) { rowBox.dataset.chReady = view.ready ? '1' : '0'; rowBox.dataset.chState = view.state; }
|
|
1865
|
+
const toggle = section.querySelector('.cc-toggle[data-ch-master]');
|
|
1866
|
+
if (toggle) {
|
|
1867
|
+
toggle.classList.toggle('on', view.on);
|
|
1868
|
+
toggle.setAttribute('aria-pressed', view.on ? 'true' : 'false');
|
|
1869
|
+
toggle.disabled = view.disabled;
|
|
1870
|
+
toggle.setAttribute('aria-describedby', `ch-master-help ch-notes${view.note ? ' ch-status-note' : ''}`);
|
|
1871
|
+
const item = toggle.closest('.cc-integration-item');
|
|
1872
|
+
item?.classList.toggle('enabled', view.on);
|
|
1873
|
+
item?.classList.toggle('uc-disabled', view.disabled);
|
|
1874
|
+
}
|
|
1875
|
+
const notes = section.querySelector('#ch-notes');
|
|
1876
|
+
if (notes) notes.innerHTML = userContextNotesHTML(view.notes);
|
|
1877
|
+
const repair = section.querySelector('#ch-repair');
|
|
1878
|
+
if (repair) repair.hidden = !(view.repair && !view.disabled);
|
|
1879
|
+
const applies = section.querySelector('#ch-applies');
|
|
1880
|
+
if (applies) applies.textContent = view.applies;
|
|
1881
|
+
const locked = !view.ready || view.disabled;
|
|
1882
|
+
for (const row of CLAUDE_HOOK_ROWS) {
|
|
1883
|
+
const rowToggle = section.querySelector(`.cc-toggle[data-cc-hook="${row.key}"]`);
|
|
1884
|
+
if (!rowToggle) continue;
|
|
1885
|
+
const on = !!view.rows?.[row.key];
|
|
1886
|
+
rowToggle.classList.toggle('on', on);
|
|
1887
|
+
rowToggle.setAttribute('aria-pressed', on ? 'true' : 'false');
|
|
1888
|
+
rowToggle.disabled = locked;
|
|
1889
|
+
const item = rowToggle.closest('.cc-integration-item');
|
|
1890
|
+
item?.classList.toggle('enabled', on);
|
|
1891
|
+
item?.classList.toggle('uc-disabled', locked);
|
|
1892
|
+
}
|
|
1893
|
+
const badge = section.querySelector('#cc-hooks-badge');
|
|
1894
|
+
if (badge) {
|
|
1895
|
+
badge.textContent = view.ready ? `${view.onRows}/${view.totalRows}` : '—';
|
|
1896
|
+
badge.classList.toggle('all-on', view.ready && view.onRows === view.totalRows);
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1827
1900
|
/** Put a view (describeUserContext) on the section: the note, each switch and its notes, the digest. */
|
|
1828
1901
|
function drawUserContext(overlay, view) {
|
|
1829
1902
|
const section = overlay.querySelector('#stg-sec-automation-behavior');
|
|
@@ -1900,16 +1973,19 @@ function tunnelExtraHtml(view, mcpKeyInfo) {
|
|
|
1900
1973
|
].filter(Boolean).join('');
|
|
1901
1974
|
}
|
|
1902
1975
|
|
|
1903
|
-
function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge, greeting, toolPermissions, toolCategories, userContext = null) {
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
const
|
|
1907
|
-
const
|
|
1908
|
-
const
|
|
1909
|
-
const
|
|
1910
|
-
const
|
|
1911
|
-
const
|
|
1912
|
-
const
|
|
1976
|
+
function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge, greeting, toolPermissions, toolCategories, userContext = null, workerGuard = null, claudeHooks = null) {
|
|
1977
|
+
// SynaBun's Claude Code hooks (settings-claude-hooks.js): the switch and the rows below show what is really in the
|
|
1978
|
+
// settings files, and nothing (disabled, with the reason) when that could not be read: never "off" by default.
|
|
1979
|
+
const ch = claudeHooks || describeClaudeHooks(null, { tx: kit.tx });
|
|
1980
|
+
const rowOn = (key) => !!ch.rows?.[key];
|
|
1981
|
+
const ssOn = rowOn('SessionStart');
|
|
1982
|
+
const psOn = rowOn('UserPromptSubmit');
|
|
1983
|
+
const pcOn = rowOn('PreCompact');
|
|
1984
|
+
const stOn = rowOn('Stop');
|
|
1985
|
+
const prOn = rowOn('PreToolUse');
|
|
1986
|
+
const ptOn = rowOn('PostToolUse');
|
|
1987
|
+
const subStartOn = rowOn('SubagentStart');
|
|
1988
|
+
const subStopOn = rowOn('SubagentStop');
|
|
1913
1989
|
const hf = ccIntegrations.hookFeatures || {};
|
|
1914
1990
|
const cmOn = hf.conversationMemory !== false;
|
|
1915
1991
|
const ulOn = hf.userLearning !== false;
|
|
@@ -1926,7 +2002,8 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
1926
2002
|
{ key: 'SubagentStart', on: subStartOn, inv: 'AUT034' }, { key: 'SubagentStop', on: subStopOn, inv: 'AUT035' },
|
|
1927
2003
|
];
|
|
1928
2004
|
const onCount = hookRows.filter(r => r.on).length;
|
|
1929
|
-
const allOn = onCount === hookRows.length;
|
|
2005
|
+
const allOn = ch.ready && onCount === hookRows.length;
|
|
2006
|
+
const hookRowsLocked = !ch.ready || ch.disabled;
|
|
1930
2007
|
|
|
1931
2008
|
const copyIcon = COPY_ICON;
|
|
1932
2009
|
|
|
@@ -1938,10 +2015,8 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
1938
2015
|
{ label: 'Claude Web', on: !!compat.web, note: compat.webNote },
|
|
1939
2016
|
{ label: 'Claude Cowork', on: !!compat.cowork },
|
|
1940
2017
|
];
|
|
1941
|
-
const
|
|
1942
|
-
{ label: '
|
|
1943
|
-
{ label: 'Gemini Code Assist', on: false },
|
|
1944
|
-
{ label: 'Gemini Web', on: false },
|
|
2018
|
+
const antigravityItems = [
|
|
2019
|
+
{ label: 'Antigravity CLI', on: false },
|
|
1945
2020
|
];
|
|
1946
2021
|
const openaiItems = [
|
|
1947
2022
|
{ label: 'Codex CLI', on: compat.codex !== false },
|
|
@@ -1970,7 +2045,7 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
1970
2045
|
};
|
|
1971
2046
|
return `<div class="cc-provider-badges" data-stg-nostatus onclick="event.stopPropagation()" ${kit.inv('AUT046')}>
|
|
1972
2047
|
${badge('anthropic', ANTHROPIC_ICON, 'compat.claude', anthropicItems, false)}
|
|
1973
|
-
${badge('google',
|
|
2048
|
+
${badge('google', ANTIGRAVITY_ICON, 'compat.antigravity', antigravityItems, true)}
|
|
1974
2049
|
${badge('openai', OPENAI_ICON, 'compat.openai', openaiItems, true)}
|
|
1975
2050
|
</div>`;
|
|
1976
2051
|
};
|
|
@@ -2051,6 +2126,44 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
2051
2126
|
<button type="button" class="conn-add-btn" id="uc-digest-all" ${kit.inv('AUT057')} title="${kit.H('AUT057')}" style="margin:0;width:auto;padding:4px 10px;border-style:solid">${kit.L('AUT057')}</button>
|
|
2052
2127
|
</div>`;
|
|
2053
2128
|
|
|
2129
|
+
// Protect workers (lib/worker-guard-setting.js): what capability read-only / workspace means for every worker the
|
|
2130
|
+
// Assistant dispatches. `workerGuard` is the view of what the server answered (describeWorkerGuard): a server
|
|
2131
|
+
// started before the setting shows the default, disabled, under a line that says a restart is missing.
|
|
2132
|
+
const wg = workerGuard || describeWorkerGuard(null, { tx: kit.tx });
|
|
2133
|
+
const workerGuardBlock = `
|
|
2134
|
+
<div class="cc-hook-toggles" id="wg-row" data-wg-ready="${wg.ready ? '1' : '0'}">
|
|
2135
|
+
<div class="cc-integration-item enabled${wg.disabled ? ' uc-disabled' : ''}" data-wg-row>
|
|
2136
|
+
<div class="cc-integration-info">
|
|
2137
|
+
<div class="cc-integration-label" id="wg-mode-label">${kit.L('AUT061')}</div>
|
|
2138
|
+
<div class="cc-integration-path" id="wg-mode-help">${kit.H('AUT061')}</div>
|
|
2139
|
+
<div class="uc-layer-notes" id="wg-notes" data-stg-data>${userContextNotesHTML(wg.note ? [wg.note] : [])}</div>
|
|
2140
|
+
<div class="stg-help" id="wg-mode-detail" data-stg-data style="margin:4px 0 0">${kit.esc(wg.options.find(o => o.value === wg.mode)?.help || '')}</div>
|
|
2141
|
+
</div>
|
|
2142
|
+
${kit.dropdown('wg-mode', wg.options.map(o => ({ value: o.value, label: kit.esc(o.label) })), { value: wg.mode, classes: 'stg-dropdown stg-dropdown-inline', label: kit.L('AUT061'), attrs: `${kit.inv('AUT061')} aria-describedby="wg-mode-help wg-notes"`, disabled: wg.disabled })}
|
|
2143
|
+
</div>
|
|
2144
|
+
</div>
|
|
2145
|
+
<div class="stg-help uc-save-status" id="wg-save-status" role="alert" data-stg-data hidden></div>
|
|
2146
|
+
<div class="stg-help" id="wg-applies">${kit.esc(wg.applies)}</div>`;
|
|
2147
|
+
|
|
2148
|
+
// SynaBun's hooks in Claude Code: one switch for all of them (POST / DELETE /api/claude-code/integrations, target
|
|
2149
|
+
// global), what is really installed under it, and Repair when the files are out of step. Outside the collapsed
|
|
2150
|
+
// card below, so it can be found; the per-moment rows stay in that card.
|
|
2151
|
+
const claudeHooksBlock = `
|
|
2152
|
+
<div class="stg-help uc-status-note" id="ch-status-note" role="status" data-ch-state="${ch.state}" data-stg-data${ch.note ? '' : ' hidden'}>${ch.note ? kit.esc(ch.note.text) : ''}</div>
|
|
2153
|
+
<div class="cc-hook-toggles" id="ch-row" data-ch-ready="${ch.ready ? '1' : '0'}" data-ch-state="${ch.state}">
|
|
2154
|
+
<div class="cc-integration-item${ch.on ? ' enabled' : ''}${ch.disabled ? ' uc-disabled' : ''}" data-ch-master-row>
|
|
2155
|
+
<div class="cc-integration-info">
|
|
2156
|
+
<div class="cc-integration-label" id="ch-master-label">${kit.L('AUT062')}</div>
|
|
2157
|
+
<div class="cc-integration-path" id="ch-master-help">${kit.H('AUT062')}</div>
|
|
2158
|
+
<div class="uc-layer-notes" id="ch-notes" data-stg-data>${userContextNotesHTML(ch.notes)}</div>
|
|
2159
|
+
<button type="button" class="cc-copy-btn stg-text-btn ch-repair" id="ch-repair" title="${kit.te('settings.redesign.hooks.claudeHooks.repair.title')}"${ch.repair && !ch.disabled ? '' : ' hidden'}>${kit.te('settings.redesign.hooks.claudeHooks.repair.action')}</button>
|
|
2160
|
+
</div>
|
|
2161
|
+
<button type="button" class="cc-toggle${ch.on ? ' on' : ''}" data-ch-master ${kit.inv('AUT062')} aria-pressed="${ch.on ? 'true' : 'false'}" aria-labelledby="ch-master-label" aria-describedby="ch-master-help ch-notes${ch.note ? ' ch-status-note' : ''}"${ch.disabled ? ' disabled' : ''}></button>
|
|
2162
|
+
</div>
|
|
2163
|
+
</div>
|
|
2164
|
+
<div class="stg-help uc-save-status" id="ch-save-status" role="alert" data-stg-data hidden></div>
|
|
2165
|
+
<div class="stg-help uc-applies" id="ch-applies">${kit.esc(ch.applies)}</div>`;
|
|
2166
|
+
|
|
2054
2167
|
return `
|
|
2055
2168
|
<div class="stg-pane" data-stg-pane="hooks">
|
|
2056
2169
|
|
|
@@ -2065,11 +2178,16 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
2065
2178
|
body: `
|
|
2066
2179
|
${userContextBlock}
|
|
2067
2180
|
|
|
2181
|
+
${workerGuardBlock}
|
|
2182
|
+
|
|
2183
|
+
${claudeHooksBlock}
|
|
2184
|
+
|
|
2068
2185
|
${kit.card({
|
|
2069
2186
|
id: 'stg-sec-hooks', level: 'h4', collapsible: true, collapsed: true, advanced: true, attrs: 'data-cc-target="global"',
|
|
2070
2187
|
title: h('hooksTitle'), purpose: h('hooksPurpose'),
|
|
2071
2188
|
// These hooks are Claude Code's own (~/.claude/settings.json, hooks/claude-code): no other assistant runs them.
|
|
2072
|
-
|
|
2189
|
+
// A count only from a state that was read: "—" otherwise, never "0/8".
|
|
2190
|
+
status: `<span class="cc-hooks-badge${allOn ? ' all-on' : ''}" id="cc-hooks-badge">${ch.ready ? `${onCount}/${hookRows.length}` : '—'}</span>${providerBadge({ cli: true, vscode: true, web: false, cowork: false, codex: false })}`,
|
|
2073
2191
|
body: `
|
|
2074
2192
|
<div class="stg-help" style="margin:0 0 10px">${h('claudeOnly')}</div>
|
|
2075
2193
|
<div class="cc-hook-toggles">
|
|
@@ -2088,12 +2206,12 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
2088
2206
|
</div>
|
|
2089
2207
|
<div class="cc-hook-toggles" style="margin-top:10px">
|
|
2090
2208
|
${hookRows.map(r => `
|
|
2091
|
-
<div class="cc-integration-item${r.on ? ' enabled' : ''}" data-hook="${r.key}">
|
|
2209
|
+
<div class="cc-integration-item${r.on ? ' enabled' : ''}${hookRowsLocked ? ' uc-disabled' : ''}" data-hook="${r.key}">
|
|
2092
2210
|
<div class="cc-integration-info">
|
|
2093
2211
|
<div class="cc-integration-label" id="cc-hook-${r.key}-label">${kit.L(r.inv)}</div>
|
|
2094
2212
|
<div class="cc-integration-path" id="cc-hook-${r.key}-help">${kit.H(r.inv)}</div>
|
|
2095
2213
|
</div>
|
|
2096
|
-
<button type="button" class="cc-toggle${r.on ? ' on' : ''}" data-cc-hook="${r.key}" data-cc-scope="global" ${kit.inv(r.inv)} aria-labelledby="cc-hook-${r.key}-label" aria-describedby="cc-hook-${r.key}-help"></button>
|
|
2214
|
+
<button type="button" class="cc-toggle${r.on ? ' on' : ''}" data-cc-hook="${r.key}" data-cc-scope="global" ${kit.inv(r.inv)} aria-pressed="${r.on ? 'true' : 'false'}" aria-labelledby="cc-hook-${r.key}-label" aria-describedby="cc-hook-${r.key}-help"${hookRowsLocked ? ' disabled' : ''}></button>
|
|
2097
2215
|
</div>`).join('')}
|
|
2098
2216
|
</div>`,
|
|
2099
2217
|
})}`,
|
|
@@ -2381,6 +2499,7 @@ function buildMoreLoginTab() {
|
|
|
2381
2499
|
<span class="settings-status-dot disconnected" id="ml-status-dot"></span>
|
|
2382
2500
|
<span id="ml-status-line">${m('checking')}</span>
|
|
2383
2501
|
</div>
|
|
2502
|
+
<div class="stg-help" id="ml-path-hint" hidden style="margin:8px 0 0">${m('customPathHint')}</div>
|
|
2384
2503
|
<div class="stg-help" style="margin:8px 0 12px">${kit.th('settings.redesign.morelogin.intro', {
|
|
2385
2504
|
link: '<a href="https://www.morelogin.com" target="_blank" rel="noopener">morelogin.com</a>',
|
|
2386
2505
|
path: `<strong>${m('apiPath')}</strong>`,
|
|
@@ -2457,7 +2576,10 @@ function wireMoreLoginTab(overlay) {
|
|
|
2457
2576
|
const dot = $('ml-status-dot'), line = $('ml-status-line'), conn = $('ml-conn-status');
|
|
2458
2577
|
if (!s.installed) { dot.className = 'settings-status-dot disconnected'; line.textContent = kit.tx('settings.redesign.morelogin.moreloginNotInstalled'); if (conn) conn.textContent = kit.tx('settings.redesign.morelogin.notInstalled'); }
|
|
2459
2578
|
else if (!s.running) { dot.className = 'settings-status-dot disconnected'; line.textContent = kit.tx('settings.redesign.morelogin.installedApiNotRunningPort', { port: s.port }); if (conn) conn.textContent = kit.tx('settings.redesign.morelogin.appClosed'); }
|
|
2579
|
+
else if (s.apiError) { dot.className = 'settings-status-dot disconnected'; line.textContent = kit.tx('settings.redesign.morelogin.runningApiRejected', { port: s.port, reason: String(s.apiError) }); if (conn) conn.textContent = kit.tx('settings.redesign.morelogin.apiError'); }
|
|
2460
2580
|
else { dot.className = 'settings-status-dot connected'; line.textContent = kit.tx('settings.redesign.morelogin.connectedProfileSPort', { profileCount: s.profileCount, port: s.port }); if (conn) conn.textContent = kit.tx('settings.redesign.morelogin.connected'); }
|
|
2581
|
+
const hint = $('ml-path-hint');
|
|
2582
|
+
if (hint) hint.hidden = !!(s.installed && s.running);
|
|
2461
2583
|
_defaultEnvId = s.defaultEnvId || null;
|
|
2462
2584
|
const ds = $('ml-default-status');
|
|
2463
2585
|
if (ds) ds.textContent = (s.isDefault && s.defaultEnvId) ? kit.tx('settings.redesign.morelogin.defaultAiBrowserSet') : kit.tx('settings.redesign.morelogin.noDefaultSet');
|
|
@@ -2503,6 +2625,11 @@ function wireMoreLoginTab(overlay) {
|
|
|
2503
2625
|
if ($('ml-api-id') && !$('ml-api-id').value) $('ml-api-id').value = cfg.config.apiId || '';
|
|
2504
2626
|
}
|
|
2505
2627
|
const p = await jget('/api/morelogin/profiles');
|
|
2628
|
+
if (p.ok === false && p.error) {
|
|
2629
|
+
const list = $('ml-profile-list');
|
|
2630
|
+
if (list) list.innerHTML = '<div class="settings-hint" role="status">' + escapeHtml(String(p.error)) + '</div>';
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2506
2633
|
renderProfiles(p.envs || []);
|
|
2507
2634
|
}
|
|
2508
2635
|
|
|
@@ -2950,7 +3077,7 @@ function activateSettingsTab(overlay, tabId = DEFAULT_SETTINGS_PAGE, { remember
|
|
|
2950
3077
|
nav.setAttribute('aria-current', 'page');
|
|
2951
3078
|
body.classList.add('active');
|
|
2952
3079
|
const picker = overlay.querySelector('#stg-page-select');
|
|
2953
|
-
if (picker) picker
|
|
3080
|
+
if (picker) setDropdownValue(picker, pageId);
|
|
2954
3081
|
if (changed) {
|
|
2955
3082
|
const content = overlay.querySelector('.settings-content');
|
|
2956
3083
|
if (content) content.scrollTop = 0;
|
|
@@ -3233,7 +3360,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
3233
3360
|
let _bridgeResult = null;
|
|
3234
3361
|
// The greeting (lib/greeting/): what its endpoint answered, as a reading (no answer is a reading too).
|
|
3235
3362
|
let greetingReading = readGreetingStatus({ status: 0 });
|
|
3236
|
-
let setupStatus = { claude: {},
|
|
3363
|
+
let setupStatus = { claude: {}, antigravity: {}, codex: {}, opencode: {}, paths: {} };
|
|
3237
3364
|
let cliConfig = {};
|
|
3238
3365
|
let toolPermissions = {};
|
|
3239
3366
|
let toolCategories = [];
|
|
@@ -3245,6 +3372,10 @@ async function openSettingsModalNow(options = {}) {
|
|
|
3245
3372
|
// The Assistant's user context (lib/assistant-user-context.js): what its endpoint answered, as a reading.
|
|
3246
3373
|
// No answer is a reading too: the section says what is true instead of showing switches that do nothing.
|
|
3247
3374
|
let userContextReading = readUserContextResponse({ status: 0 });
|
|
3375
|
+
// Protect workers (lib/worker-guard-setting.js): what its endpoint answered, as a reading.
|
|
3376
|
+
let workerGuardReading = readWorkerGuardResponse({ status: 0 });
|
|
3377
|
+
// SynaBun's Claude Code hooks (lib/claude-hooks-status.js): the `claudeHooks` of the integrations read, as a reading.
|
|
3378
|
+
let claudeHooksReading = readClaudeHooksResponse({ status: 0 });
|
|
3248
3379
|
|
|
3249
3380
|
try {
|
|
3250
3381
|
// Read through the page's data cache (settings/settings-data.js): kept answers at once (each open its own
|
|
@@ -3254,15 +3385,19 @@ async function openSettingsModalNow(options = {}) {
|
|
|
3254
3385
|
const readingOf = (entry, make) => ({ status: 'fulfilled', value: entry?.status === 'fulfilled' ? make(entry.value) : make({ status: 0, error: entry?.reason?.message }) });
|
|
3255
3386
|
const greetRes = readingOf(reads.greeting, readGreetingStatus);
|
|
3256
3387
|
const userContextRes = readingOf(reads.userContext, readUserContextResponse);
|
|
3388
|
+
const workerGuardRes = readingOf(reads.workerGuard, readWorkerGuardResponse);
|
|
3257
3389
|
const settingsRes = reads.settings, connRes = reads.connections, ccRes = reads.integrations, skillsRes = reads.skills;
|
|
3258
3390
|
const tunnelRes = reads.tunnel, keyRes = reads.mcpKey, bridgeRes = reads.openclaw, setupRes = reads.setup, cliRes = reads.cli;
|
|
3259
3391
|
const toolPermsRes = reads.toolPermissions, toolCatsRes = reads.toolCategories, discordRes = reads.discord, skinsRes = reads.skins;
|
|
3260
3392
|
const iconsRes = reads.icons, youtubeRes = reads.youtube, typesafeRes = reads.typesafe;
|
|
3261
3393
|
if (typesafeRes.status === 'fulfilled' && typesafeRes.value.ok) typesafeConfig = typesafeRes.value;
|
|
3262
3394
|
if (userContextRes.status === 'fulfilled') userContextReading = userContextRes.value;
|
|
3395
|
+
if (workerGuardRes.status === 'fulfilled') workerGuardReading = workerGuardRes.value;
|
|
3263
3396
|
if (settingsRes.status === 'fulfilled') settings = settingsRes.value;
|
|
3264
3397
|
if (connRes.status === 'fulfilled' && connRes.value.connections) connections = connRes.value.connections;
|
|
3265
|
-
|
|
3398
|
+
// Read as it came (status and body): a refusal or an old server is said as what it is, never as hooks that are off.
|
|
3399
|
+
if (ccRes.status === 'fulfilled' && ccRes.value?.status >= 200 && ccRes.value.status < 300 && ccRes.value.body?.ok) ccIntegrations = ccRes.value.body;
|
|
3400
|
+
claudeHooksReading = readingOf(ccRes, readClaudeHooksResponse).value;
|
|
3266
3401
|
if (skillsRes.status === 'fulfilled' && skillsRes.value.ok) ccSkills = skillsRes.value.skills || [];
|
|
3267
3402
|
if (tunnelRes.status === 'fulfilled' && tunnelRes.value.ok) tunnelStatus = tunnelRes.value;
|
|
3268
3403
|
if (keyRes.status === 'fulfilled' && keyRes.value.ok) mcpKeyInfo = keyRes.value;
|
|
@@ -3295,9 +3430,10 @@ async function openSettingsModalNow(options = {}) {
|
|
|
3295
3430
|
overlay.className = 'settings-panel glass resizable';
|
|
3296
3431
|
overlay.id = 'settings-panel';
|
|
3297
3432
|
|
|
3298
|
-
// Always open centered at default size
|
|
3299
|
-
|
|
3300
|
-
overlay.style.
|
|
3433
|
+
// Always open centered at default size, in the app frame
|
|
3434
|
+
const at = centeredInFrame(900, 650);
|
|
3435
|
+
overlay.style.left = at.left + 'px';
|
|
3436
|
+
overlay.style.top = at.top + 'px';
|
|
3301
3437
|
|
|
3302
3438
|
// ── Assemble HTML ──
|
|
3303
3439
|
overlay.innerHTML = `
|
|
@@ -3330,7 +3466,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
3330
3466
|
<div class="settings-content">
|
|
3331
3467
|
${buildPagesHTML({
|
|
3332
3468
|
server: buildServerTab(settings, connections),
|
|
3333
|
-
hooks: buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge, describeGreeting(greetingReading, null, { tx: kit.tx, t: kit.tx }), toolPermissions, toolCategories, describeUserContext(userContextReading, { tx: kit.tx })),
|
|
3469
|
+
hooks: buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge, describeGreeting(greetingReading, null, { tx: kit.tx, t: kit.tx }), toolPermissions, toolCategories, describeUserContext(userContextReading, { tx: kit.tx }), describeWorkerGuard(workerGuardReading, { tx: kit.tx }), describeClaudeHooks(claudeHooksReading, { tx: kit.tx })),
|
|
3334
3470
|
terminal: buildTerminalTab(cliConfig),
|
|
3335
3471
|
opencode: buildOpencodeTab(),
|
|
3336
3472
|
notifications: buildNotificationsTab(),
|
|
@@ -3375,7 +3511,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
3375
3511
|
|
|
3376
3512
|
// ── Nav status dots (Judgments and WhatsApp update theirs as they wire) ──
|
|
3377
3513
|
kit.setNavStatus(overlay, 'server', settings.storage === 'sqlite' ? 'connected' : 'disconnected');
|
|
3378
|
-
kit.setNavStatus(overlay, 'setup', (setupStatus.claude?.connected || setupStatus.
|
|
3514
|
+
kit.setNavStatus(overlay, 'setup', (setupStatus.claude?.connected || setupStatus.antigravity?.connected || setupStatus.codex?.connected || setupStatus.opencode?.connected) ? 'connected' : 'disconnected');
|
|
3379
3515
|
kit.setNavStatus(overlay, 'discord', discordConfig.botToken ? 'connected' : 'disconnected');
|
|
3380
3516
|
kit.setNavStatus(overlay, 'judgments', (typesafeConfig && typesafeConfig.enabled) ? 'connected' : 'disconnected');
|
|
3381
3517
|
|
|
@@ -4592,7 +4728,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
4592
4728
|
// Must stay in sync with CLI_DEFAULTS in server.js and the cliProfiles
|
|
4593
4729
|
// list that renders the inputs — opencode was previously missing here, so
|
|
4594
4730
|
// its input rendered but its value was never sent to the server.
|
|
4595
|
-
const cliDefaults = { 'claude-code': 'claude', 'codex': 'codex', '
|
|
4731
|
+
const cliDefaults = { 'claude-code': 'claude', 'codex': 'codex', 'antigravity': 'agy', 'opencode': 'opencode' };
|
|
4596
4732
|
const cliProfileIds = Object.keys(cliDefaults);
|
|
4597
4733
|
|
|
4598
4734
|
const pathButtons = overlay.querySelectorAll('[data-cli-add-path]');
|
|
@@ -4639,9 +4775,14 @@ async function openSettingsModalNow(options = {}) {
|
|
|
4639
4775
|
});
|
|
4640
4776
|
const data = await res.json();
|
|
4641
4777
|
showPathAction(profileId, data);
|
|
4778
|
+
const refusal = profileId === 'opencode' && data.support ? describeOpencodeInstall(data.support, { th: kit.th, tx: kit.tx }) : null;
|
|
4642
4779
|
if (data.ok && data.found && data.path && data.state === 'broken') {
|
|
4643
4780
|
// Found, but it does not start: not put in the field, so a broken path is never saved by accident.
|
|
4644
4781
|
showCCToast(kit.tx('settings.redesign.terminal.detectionFailed', { message: data.error ? `${data.path}: ${data.error}` : data.path }));
|
|
4782
|
+
} else if (data.ok && data.found && data.path && refusal?.status === 'notSupported') {
|
|
4783
|
+
// An OpenCode SynaBun does not drive (OpenCode 1, say): put in the field, with the sentence that says why it will not start.
|
|
4784
|
+
input.value = data.path;
|
|
4785
|
+
showCCToast(refusal.text, 12000);
|
|
4645
4786
|
} else if (data.ok && data.found && data.path) {
|
|
4646
4787
|
input.value = data.path;
|
|
4647
4788
|
showCCToast(kit.tx('settings.redesign.terminal.found', { path: data.version ? `${data.path} (${data.version})` : data.path }));
|
|
@@ -4899,9 +5040,15 @@ async function openSettingsModalNow(options = {}) {
|
|
|
4899
5040
|
if (data.ok) {
|
|
4900
5041
|
const nowOn = !isOn;
|
|
4901
5042
|
toggleBtn.classList.toggle('on');
|
|
5043
|
+
toggleBtn.setAttribute('aria-pressed', nowOn ? 'true' : 'false');
|
|
4902
5044
|
const row = toggleBtn.closest('.cc-integration-item');
|
|
4903
5045
|
if (row) row.classList.toggle('enabled', nowOn);
|
|
4904
|
-
if (target === 'global') {
|
|
5046
|
+
if (target === 'global') {
|
|
5047
|
+
updateGlobalHookBadge();
|
|
5048
|
+
// The answer's state of every hook (a server from before it sends none): the switch above follows it.
|
|
5049
|
+
if (data.claudeHooks) { claudeHooksReading = readClaudeHooksResponse({ status: res.status, body: { ok: true, claudeHooks: data.claudeHooks } }); redrawClaudeHooks(); }
|
|
5050
|
+
refreshSettingsData(['integrations']);
|
|
5051
|
+
}
|
|
4905
5052
|
else { toggleBtn.textContent = nowOn ? kit.tx('settings.redesign.hooks.enabled') : kit.tx('settings.redesign.hooks.enable'); if (panel) panel.classList.toggle('enabled'); const badge = panel?.querySelector('.cc-panel-status'); if (badge) { badge.textContent = nowOn ? kit.tx('settings.redesign.hooks.active') : kit.tx('settings.redesign.hooks.off'); badge.className = 'cc-panel-status ' + (nowOn ? 'active' : 'inactive'); } updateProjectCount(); }
|
|
4906
5053
|
} else { alert(data.error || kit.tx('settings.redesign.hooks.failedToToggleHook')); }
|
|
4907
5054
|
} catch (err) { alert(kit.tx('settings.redesign.hooks.failed2', { message: err.message })); }
|
|
@@ -4913,6 +5060,41 @@ async function openSettingsModalNow(options = {}) {
|
|
|
4913
5060
|
toggle.addEventListener('click', () => { ccToggleHook('global', null, toggle, overlay.querySelector('.iface-section[data-cc-target="global"]'), toggle.dataset.ccHook); });
|
|
4914
5061
|
});
|
|
4915
5062
|
|
|
5063
|
+
// ── SynaBun hooks in Claude Code: one switch for all of them, and Repair ──
|
|
5064
|
+
// Each change is the server's: the row is drawn again from the state its answer carries (what the files hold now),
|
|
5065
|
+
// and a change that failed leaves the row as it was, with the reason under it. One change at a time.
|
|
5066
|
+
function redrawClaudeHooks() { drawClaudeHooks(overlay, describeClaudeHooks(claudeHooksReading, { tx: kit.tx })); }
|
|
5067
|
+
const setClaudeHooksSaveStatus = (text = '') => {
|
|
5068
|
+
const status = overlay.querySelector('#ch-save-status');
|
|
5069
|
+
if (status) { status.hidden = !text; status.textContent = text; }
|
|
5070
|
+
};
|
|
5071
|
+
let claudeHooksBusy = false;
|
|
5072
|
+
async function changeClaudeHooks(action) {
|
|
5073
|
+
if (claudeHooksBusy || claudeHooksReading.state !== 'ready') return;
|
|
5074
|
+
claudeHooksBusy = true;
|
|
5075
|
+
setClaudeHooksSaveStatus('');
|
|
5076
|
+
const row = overlay.querySelector('[data-ch-master-row]');
|
|
5077
|
+
if (row) row.style.opacity = '0.6';
|
|
5078
|
+
let failure = null;
|
|
5079
|
+
try {
|
|
5080
|
+
// On and Repair ask for the hooks to run (unblock: Claude Code's own disableAllHooks in the same file goes too).
|
|
5081
|
+
const payload = action === 'off' ? { target: 'global' } : { target: 'global', unblock: true };
|
|
5082
|
+
const res = await fetch('/api/claude-code/integrations', { method: action === 'off' ? 'DELETE' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
|
|
5083
|
+
const body = await res.json().catch(() => null);
|
|
5084
|
+
if (res.ok && body?.ok) {
|
|
5085
|
+
claudeHooksReading = readClaudeHooksResponse({ status: res.status, body: { ok: true, claudeHooks: body.claudeHooks } });
|
|
5086
|
+
refreshSettingsData(['integrations']);
|
|
5087
|
+
} else failure = { status: res.status, body };
|
|
5088
|
+
} catch (err) { failure = { status: 0, error: err?.message }; }
|
|
5089
|
+
finally { claudeHooksBusy = false; if (row) row.style.opacity = ''; }
|
|
5090
|
+
redrawClaudeHooks();
|
|
5091
|
+
if (failure) setClaudeHooksSaveStatus(describeClaudeHooksSaveFailure({ action, ...failure }, { tx: kit.tx }));
|
|
5092
|
+
}
|
|
5093
|
+
overlay.querySelector('.cc-toggle[data-ch-master]')?.addEventListener('click', (e) => {
|
|
5094
|
+
changeClaudeHooks(e.currentTarget.classList.contains('on') ? 'off' : 'on');
|
|
5095
|
+
});
|
|
5096
|
+
overlay.querySelector('#ch-repair')?.addEventListener('click', () => changeClaudeHooks('repair'));
|
|
5097
|
+
|
|
4916
5098
|
// ── The Assistant's user context: layers and the digest preview ──
|
|
4917
5099
|
const ucRowsEl = overlay.querySelector('#uc-digest-rows');
|
|
4918
5100
|
const redrawUserContext = (reading = userContextReading) => drawUserContext(overlay, describeUserContext(reading, { tx: kit.tx }));
|
|
@@ -4939,6 +5121,47 @@ async function openSettingsModalNow(options = {}) {
|
|
|
4939
5121
|
if (!link) return;
|
|
4940
5122
|
try { applySettingsOpenOptions(overlay, JSON.parse(link.dataset.ucGoto)); } catch { /* a malformed target goes nowhere */ }
|
|
4941
5123
|
});
|
|
5124
|
+
// Protect workers: the choice is saved at once and follows the server's answer; a save that failed puts the
|
|
5125
|
+
// dropdown back where it was, with the reason under it. Saves go one after the other, in the order of the picks.
|
|
5126
|
+
{
|
|
5127
|
+
const input = overlay.querySelector('#wg-mode');
|
|
5128
|
+
const dd = overlay.querySelector('.cc-dropdown[data-for="wg-mode"]');
|
|
5129
|
+
const status = overlay.querySelector('#wg-save-status');
|
|
5130
|
+
const detail = overlay.querySelector('#wg-mode-detail');
|
|
5131
|
+
const setStatus = (text) => { if (status) { status.textContent = text; status.hidden = !text; } };
|
|
5132
|
+
const showMode = (mode) => {
|
|
5133
|
+
if (input) input.value = mode;
|
|
5134
|
+
const view = describeWorkerGuard(workerGuardReading, { tx: kit.tx });
|
|
5135
|
+
const option = view.options.find(o => o.value === mode);
|
|
5136
|
+
dd?.querySelector('.cc-dropdown-value') && (dd.querySelector('.cc-dropdown-value').textContent = option?.label || mode);
|
|
5137
|
+
dd?.querySelectorAll('.cc-dropdown-item').forEach(item => item.classList.toggle('active', item.dataset.value === mode));
|
|
5138
|
+
if (detail) detail.textContent = option?.help || '';
|
|
5139
|
+
};
|
|
5140
|
+
let saves = Promise.resolve();
|
|
5141
|
+
input?.addEventListener('change', () => {
|
|
5142
|
+
if (workerGuardReading.state !== 'ready') return;
|
|
5143
|
+
const mode = String(input.value || '');
|
|
5144
|
+
if (!mode || mode === workerGuardReading.mode) { showMode(workerGuardReading.mode); return; }
|
|
5145
|
+
setStatus('');
|
|
5146
|
+
showMode(mode);
|
|
5147
|
+
if (dd) dd.style.opacity = '0.4';
|
|
5148
|
+
saves = saves.then(async () => {
|
|
5149
|
+
let failure = null;
|
|
5150
|
+
try {
|
|
5151
|
+
const res = await fetch('/api/assistant/worker-guard', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ mode }) });
|
|
5152
|
+
const body = await res.json().catch(() => null);
|
|
5153
|
+
const reading = readWorkerGuardResponse({ status: res.status, body });
|
|
5154
|
+
if (res.ok && reading.state === 'ready') workerGuardReading = reading;
|
|
5155
|
+
else failure = { status: res.status, body };
|
|
5156
|
+
} catch (err) {
|
|
5157
|
+
failure = { status: 0, error: err?.message };
|
|
5158
|
+
}
|
|
5159
|
+
if (dd) dd.style.opacity = '';
|
|
5160
|
+
showMode(workerGuardReading.mode);
|
|
5161
|
+
if (failure) setStatus(describeWorkerGuardSaveFailure({ ...failure, mode }, { tx: kit.tx }));
|
|
5162
|
+
});
|
|
5163
|
+
});
|
|
5164
|
+
}
|
|
4942
5165
|
// The switch moves at once and follows the server's answer: the stored settings when the save went through,
|
|
4943
5166
|
// back where it was (with the reason next to the switches) when it did not. Saves go one after the other, in
|
|
4944
5167
|
// the order of the clicks, so a second click made before the first was answered is kept, not dropped.
|
|
@@ -5192,8 +5415,14 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5192
5415
|
btn.addEventListener('click', () => { const idx = btn.dataset.ccProjectToggle; const panel = overlay.querySelector(`.cc-panel[data-cc-idx="${idx}"]`); const projectPath = panel?.dataset.ccPath; if (projectPath) ccToggleHook('project', projectPath, btn, panel); });
|
|
5193
5416
|
});
|
|
5194
5417
|
|
|
5195
|
-
//
|
|
5196
|
-
|
|
5418
|
+
// Skills Studio, from Settings → Skills: Settings closes, the Studio opens on its Library.
|
|
5419
|
+
overlay.querySelector('[data-cc-open-studio]')?.addEventListener('click', () => {
|
|
5420
|
+
close();
|
|
5421
|
+
emit('skills:open', { place: 'library' });
|
|
5422
|
+
});
|
|
5423
|
+
|
|
5424
|
+
// Skill target chips — one skill can be installed independently into Claude / Codex / OpenCode / Antigravity
|
|
5425
|
+
const runtimeLabels = { claude: 'Claude Code', codex: 'Codex CLI', opencode: 'OpenCode CLI', antigravity: 'Antigravity CLI' };
|
|
5197
5426
|
overlay.querySelectorAll('.cc-skill-target[data-cc-skill]').forEach(chip => {
|
|
5198
5427
|
chip.addEventListener('click', async () => {
|
|
5199
5428
|
const skillName = chip.dataset.ccSkill;
|
|
@@ -5201,28 +5430,48 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5201
5430
|
const isOn = chip.classList.contains('on');
|
|
5202
5431
|
try {
|
|
5203
5432
|
chip.style.opacity = '0.4'; chip.style.pointerEvents = 'none';
|
|
5204
|
-
const
|
|
5433
|
+
const send = (extra = {}) => fetch('/api/skills/install', {
|
|
5205
5434
|
method: isOn ? 'DELETE' : 'POST',
|
|
5206
5435
|
headers: { 'Content-Type': 'application/json' },
|
|
5207
|
-
body: JSON.stringify({ name: skillName, target }),
|
|
5208
|
-
});
|
|
5209
|
-
|
|
5436
|
+
body: JSON.stringify({ name: skillName, target, ...extra }),
|
|
5437
|
+
}).then((res) => res.json());
|
|
5438
|
+
let data = await send();
|
|
5439
|
+
// A skill of that name that SynaBun did not install, or that was changed since, is neither replaced nor
|
|
5440
|
+
// removed unasked: the server says what it is, and only a yes sends its digest back (a backup is kept).
|
|
5441
|
+
if (!data.ok && data.currentDigest && (data.code === 'skill_unowned' || data.code === 'skill_modified')) {
|
|
5442
|
+
if (!confirm(data.question || data.error)) return;
|
|
5443
|
+
data = await send({ expectedDestinationDigest: data.currentDigest });
|
|
5444
|
+
}
|
|
5210
5445
|
if (data.ok) {
|
|
5211
5446
|
chip.classList.toggle('on');
|
|
5212
5447
|
chip.dataset.state = isOn ? 'off' : 'on';
|
|
5448
|
+
// (a server from before the skill folders answers without `destination`: the chip then only says it is installed)
|
|
5449
|
+
const now = chipState(isOn ? null : (data.destination ? (data.shared ? { native: 'absent', shared: 'native' } : { native: 'native', bridge: !!data.bridge }) : undefined), !isOn);
|
|
5450
|
+
chip.dataset.installState = now.state;
|
|
5451
|
+
chip.setAttribute('aria-pressed', now.on ? 'true' : 'false');
|
|
5452
|
+
chip.title = kit.tx(now.key, { skill: skillName, assistant: chip.textContent.trim() });
|
|
5213
5453
|
const row = chip.closest('.cc-skill-row');
|
|
5214
5454
|
if (row) {
|
|
5215
5455
|
const anyOn = !!row.querySelector('.cc-skill-target.on');
|
|
5216
5456
|
row.classList.toggle('installed', anyOn);
|
|
5217
5457
|
}
|
|
5218
5458
|
const runtime = runtimeLabels[target] || target;
|
|
5219
|
-
|
|
5459
|
+
// The shared ~/.agents/skills is read by Codex and OpenCode both: the other chips of the skill follow.
|
|
5460
|
+
if (data.shared) refreshSkillChips(overlay);
|
|
5461
|
+
// An install is a native skill folder now; the server says when an old bridge of it is still there.
|
|
5462
|
+
if (isOn) showCCToast(kit.tx('settings.redesign.hooks.skillUninstalledRestartToApply', { runtime: runtime }));
|
|
5463
|
+
else if (data.destination) showCCToast(kit.tx(installedToast(data), { skill: skillName, runtime: runtime }), 6000);
|
|
5464
|
+
else showCCToast(kit.tx('settings.redesign.hooks.skillInstalledRestartToApply', { runtime: runtime }));
|
|
5220
5465
|
} else { alert(data.error || kit.tx('settings.redesign.hooks.failedToToggleSkill')); }
|
|
5221
5466
|
} catch (err) { alert(kit.tx('settings.redesign.hooks.failed2', { message: err.message })); }
|
|
5222
5467
|
finally { chip.style.opacity = ''; chip.style.pointerEvents = ''; }
|
|
5223
5468
|
});
|
|
5224
5469
|
});
|
|
5225
5470
|
|
|
5471
|
+
// Skill folders: the move of the old bridges to native skill folders, and the shared ~/.agents/skills switch.
|
|
5472
|
+
// After a move, an undo, or a change made anywhere else, the chips are read again in place.
|
|
5473
|
+
wireSkillFolders(overlay, { toast: (message, ms) => showCCToast(message, ms), onChanged: () => refreshSkillChips(overlay), signal: panelLife.signal });
|
|
5474
|
+
|
|
5226
5475
|
// ── Setup tab: per-provider MCP toggles, CLI copy, config copy, SynaBun rules ──
|
|
5227
5476
|
{
|
|
5228
5477
|
const checkIcon = '<svg viewBox="0 0 24 24" style="width:12px;height:12px"><polyline points="20 6 9 17 4 12"/></svg>';
|
|
@@ -5378,7 +5627,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5378
5627
|
// An edited copy is removed only on an explicit yes (force). A file without SynaBun's markers is the
|
|
5379
5628
|
// user's own and stays whatever is sent, so there is nothing to confirm for it.
|
|
5380
5629
|
const edited = info?.state === 'modified' && info.replaceable !== false;
|
|
5381
|
-
// OpenCode: a line in
|
|
5630
|
+
// OpenCode: a line in opencode.json that SynaBun did not add keeps the file on a plain remove (the answer
|
|
5382
5631
|
// says so, and the host is then no longer managed). Asked again, the confirm sends force, which takes that line too.
|
|
5383
5632
|
const listed = info?.entry === 'user';
|
|
5384
5633
|
const force = edited || (listed && info.managed === false);
|
|
@@ -5400,7 +5649,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5400
5649
|
const message = overlay.querySelector('#setup-rules-message');
|
|
5401
5650
|
const legacyBox = overlay.querySelector('#setup-rules-legacy');
|
|
5402
5651
|
const versionBadge = overlay.querySelector('#setup-rules-version');
|
|
5403
|
-
const hostOf = { claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode',
|
|
5652
|
+
const hostOf = { claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode', antigravity: 'Antigravity' };
|
|
5404
5653
|
|
|
5405
5654
|
if (autoToggle && installAllBtn && legacyBox) {
|
|
5406
5655
|
rulesRenderers.push(() => {
|
|
@@ -5473,26 +5722,26 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5473
5722
|
: { command: 'node', args: [paths.mcpIndexPath || '<path-to>/mcp-server/run.mjs'] };
|
|
5474
5723
|
};
|
|
5475
5724
|
|
|
5476
|
-
// ──
|
|
5477
|
-
wireSetupMcpToggle('
|
|
5478
|
-
// Config preview
|
|
5725
|
+
// ── Antigravity ──
|
|
5726
|
+
wireSetupMcpToggle('antigravity', '/api/setup/antigravity/mcp', '~/.gemini/config/mcp_config.json');
|
|
5727
|
+
// Config preview (mcp_config.json: `disabled`, no `trust`)
|
|
5479
5728
|
{
|
|
5480
|
-
const preview = overlay.querySelector('#setup-
|
|
5729
|
+
const preview = overlay.querySelector('#setup-antigravity-config-preview');
|
|
5481
5730
|
let cachedConfig = '';
|
|
5482
5731
|
if (preview) {
|
|
5483
|
-
fetch('/api/setup/
|
|
5732
|
+
fetch('/api/setup/antigravity/mcp').then(r => r.json()).then(data => {
|
|
5484
5733
|
if (data.ok && data.config) {
|
|
5485
5734
|
cachedConfig = JSON.stringify(data.config, null, 2);
|
|
5486
5735
|
preview.textContent = cachedConfig;
|
|
5487
5736
|
} else if (data.ok) {
|
|
5488
|
-
cachedConfig = JSON.stringify({ mcpServers: { SynaBun: { ...mcpLaunch(), env: { DOTENV_PATH: setupStatus.paths?.envPath || '<path-to>/synabun/.env' } } } }, null, 2);
|
|
5737
|
+
cachedConfig = JSON.stringify({ mcpServers: { SynaBun: { ...mcpLaunch(), env: { DOTENV_PATH: setupStatus.paths?.envPath || '<path-to>/synabun/.env' }, disabled: false } } }, null, 2);
|
|
5489
5738
|
preview.textContent = cachedConfig;
|
|
5490
5739
|
} else { preview.textContent = kit.tx('settings.redesign.setup.couldNotLoadConfig'); }
|
|
5491
5740
|
}).catch(() => { preview.textContent = kit.tx('settings.redesign.setup.failedToLoad'); });
|
|
5492
5741
|
}
|
|
5493
|
-
wireCopyBtn('setup-
|
|
5742
|
+
wireCopyBtn('setup-antigravity-config-copy', () => cachedConfig, kit.tx('settings.redesign.setup.copyJsonConfig'));
|
|
5494
5743
|
}
|
|
5495
|
-
wireRulesControls('
|
|
5744
|
+
wireRulesControls('antigravity', 'antigravity');
|
|
5496
5745
|
|
|
5497
5746
|
// ── Codex ──
|
|
5498
5747
|
wireSetupMcpToggle('codex', '/api/setup/codex/mcp', '~/.codex/config.toml');
|
|
@@ -5521,8 +5770,8 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5521
5770
|
wireRulesControls('codex', 'codex');
|
|
5522
5771
|
|
|
5523
5772
|
// ── OpenCode ──
|
|
5524
|
-
wireSetupMcpToggle('opencode', '/api/opencode/mcp', '~/.config/opencode/
|
|
5525
|
-
// Config preview — OpenCode
|
|
5773
|
+
wireSetupMcpToggle('opencode', '/api/opencode/mcp', '~/.config/opencode/opencode.json');
|
|
5774
|
+
// Config preview — OpenCode 2 keeps MCP servers under "mcp" → "servers" of opencode.json
|
|
5526
5775
|
{
|
|
5527
5776
|
const preview = overlay.querySelector('#setup-opencode-config-preview');
|
|
5528
5777
|
let cachedConfig = '';
|
|
@@ -5530,15 +5779,18 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5530
5779
|
const ep = setupStatus.paths?.envPath || '<path-to>/synabun/.env';
|
|
5531
5780
|
fetch('/api/opencode/mcp').then(r => r.json()).then(data => {
|
|
5532
5781
|
if (data.ok && data.data && Object.keys(data.data).length) {
|
|
5533
|
-
cachedConfig = JSON.stringify({ mcp: data.data }, null, 2);
|
|
5782
|
+
cachedConfig = JSON.stringify({ mcp: { servers: data.data } }, null, 2);
|
|
5534
5783
|
preview.textContent = cachedConfig;
|
|
5535
5784
|
} else if (data.ok) {
|
|
5785
|
+
const launch = mcpLaunch();
|
|
5536
5786
|
cachedConfig = JSON.stringify({
|
|
5537
5787
|
mcp: {
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5788
|
+
servers: {
|
|
5789
|
+
SynaBun: {
|
|
5790
|
+
type: 'local',
|
|
5791
|
+
command: [launch.command, ...launch.args],
|
|
5792
|
+
environment: { DOTENV_PATH: ep },
|
|
5793
|
+
},
|
|
5542
5794
|
},
|
|
5543
5795
|
},
|
|
5544
5796
|
}, null, 2);
|
|
@@ -5810,16 +6062,18 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5810
6062
|
let _moreloginDesiredEnvId = '';
|
|
5811
6063
|
async function loadMoreLoginEnvs() {
|
|
5812
6064
|
const sel = overlay.querySelector('#bc-morelogin-env-id');
|
|
6065
|
+
// A placeholder choice (loading / error / not running) keeps its reason visible in the closed dropdown.
|
|
6066
|
+
const only = (key) => setDropdownItems(sel, [{ value: '', label: kit.tx(key), selected: true }]);
|
|
5813
6067
|
const statusEl = overlay.querySelector('#bc-morelogin-status-text');
|
|
5814
6068
|
if (!sel) return;
|
|
5815
6069
|
try {
|
|
5816
6070
|
const r = await fetch('/api/browser/morelogin-envs');
|
|
5817
6071
|
const d = await r.json();
|
|
5818
|
-
if (!d || d.ok === false) {
|
|
5819
|
-
if (!d.installed) {
|
|
5820
|
-
if (!d.running) {
|
|
6072
|
+
if (!d || d.ok === false) { only('settings.redesign.browser.error'); if (statusEl) statusEl.textContent = (d && d.error) || kit.tx('settings.redesign.browser.failedToLoadMoreloginProfiles'); return; }
|
|
6073
|
+
if (!d.installed) { only('settings.redesign.browser.moreloginNotInstalled'); if (statusEl) statusEl.textContent = kit.tx('settings.redesign.browser.moreloginDesktopAppIsNotInstalled') + ' ' + kit.tx('settings.redesign.browser.moreloginCustomPathHint'); return; }
|
|
6074
|
+
if (!d.running) { only('settings.redesign.browser.startMorelogin'); if (statusEl) statusEl.textContent = kit.tx('settings.redesign.browser.moreloginNotRunningOpenTheApp', { port: d.port }) + (d.error ? ' ' + String(d.error) : ''); return; }
|
|
5821
6075
|
const envs = d.envs || [];
|
|
5822
|
-
if (!envs.length) {
|
|
6076
|
+
if (!envs.length) { only('settings.redesign.browser.noProfiles'); if (statusEl) statusEl.textContent = kit.tx('settings.redesign.browser.noMoreloginProfilesYetOneIs'); return; }
|
|
5823
6077
|
const want = _moreloginDesiredEnvId || sel.value || '';
|
|
5824
6078
|
// Show each env's proxy — without it there is no way to tell from SynaBun
|
|
5825
6079
|
// whether a profile's traffic leaves the machine.
|
|
@@ -5828,8 +6082,9 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5828
6082
|
const where = [p.host, p.port].filter(Boolean).join(':');
|
|
5829
6083
|
return ` · ${[p.type, where, p.country].filter(Boolean).join(' ')}`;
|
|
5830
6084
|
};
|
|
5831
|
-
|
|
5832
|
-
|
|
6085
|
+
// dropdownHTML escapes the labels itself.
|
|
6086
|
+
setDropdownItems(sel, envs.map(e => ({ value: String(e.id), label: `${e.name ?? ''}${e.status ? ` (${String(e.status)})` : ''}${proxyLabel(e.proxy)}` })));
|
|
6087
|
+
if (want && envs.some(e => String(e.id) === String(want))) setDropdownValue(sel, String(want));
|
|
5833
6088
|
if (statusEl) {
|
|
5834
6089
|
let msg = kit.tx('settings.redesign.browser.profileSApiPort', { count: envs.length, port: d.port });
|
|
5835
6090
|
// MoreLogin silently redirects a navigation it refuses to its stop page: a
|
|
@@ -5844,7 +6099,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5844
6099
|
statusEl.textContent = msg;
|
|
5845
6100
|
}
|
|
5846
6101
|
} catch {
|
|
5847
|
-
|
|
6102
|
+
only('settings.redesign.browser.error');
|
|
5848
6103
|
if (statusEl) statusEl.textContent = kit.tx('settings.redesign.browser.failedToReachMorelogin');
|
|
5849
6104
|
}
|
|
5850
6105
|
}
|
|
@@ -5892,6 +6147,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
5892
6147
|
item.addEventListener('click', refreshAttachSeedRow);
|
|
5893
6148
|
});
|
|
5894
6149
|
}
|
|
6150
|
+
{ const mlRow = overlay.querySelector('#bc-morelogin-row'); if (mlRow) wireDropdowns(mlRow, { signal: panelLife.signal }); }
|
|
5895
6151
|
overlay.querySelector('#bc-morelogin-refresh')?.addEventListener('click', (e) => { e.preventDefault(); loadMoreLoginEnvs(); });
|
|
5896
6152
|
const attachSeedDd = overlay.querySelector('.bc-dropdown[data-for="bc-attachSeed"]');
|
|
5897
6153
|
if (attachSeedDd) {
|
|
@@ -7032,7 +7288,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7032
7288
|
|
|
7033
7289
|
// Explore project
|
|
7034
7290
|
function openExploreModal(projectPath, projectLabel) {
|
|
7035
|
-
const EXPLORE_CLI_IDS = ['claude-code', 'codex', '
|
|
7291
|
+
const EXPLORE_CLI_IDS = ['claude-code', 'codex', 'antigravity'];
|
|
7036
7292
|
const CLI_OPTIONS = CLI_PROFILES
|
|
7037
7293
|
.filter(p => EXPLORE_CLI_IDS.includes(p.id))
|
|
7038
7294
|
.map(p => ({ id: p.id, label: p.label }));
|
|
@@ -7112,7 +7368,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7112
7368
|
selectedCli = cli;
|
|
7113
7369
|
selectedModel = getExploreModels(cli)[0]?.id || '';
|
|
7114
7370
|
renderModal();
|
|
7115
|
-
if (cli === 'codex') {
|
|
7371
|
+
if (cli === 'codex' || cli === 'antigravity') {
|
|
7116
7372
|
ensureModelsForProfile(cli).then(() => {
|
|
7117
7373
|
if (exploreOverlay.isConnected && selectedCli === cli) renderModal();
|
|
7118
7374
|
});
|
|
@@ -7315,6 +7571,9 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7315
7571
|
const ocpToolsEl = overlay.querySelector('#stg-ocp-tools');
|
|
7316
7572
|
const ocpHistoryMetaEl = overlay.querySelector('#stg-ocp-history-meta');
|
|
7317
7573
|
const ocpHistoryClearBtn = overlay.querySelector('#stg-ocp-clear-history');
|
|
7574
|
+
const ocpInstallEl = overlay.querySelector('#stg-ocp-install');
|
|
7575
|
+
const ocpConfigPathEl = overlay.querySelector('#stg-ocp-config-path');
|
|
7576
|
+
const ocpConfigJsoncEl = overlay.querySelector('#stg-ocp-config-jsonc');
|
|
7318
7577
|
const esc = s => String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
7319
7578
|
|
|
7320
7579
|
// ── Helpers ──
|
|
@@ -7337,6 +7596,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7337
7596
|
let _ollamaConfiguring = false;
|
|
7338
7597
|
let _ollamaPollTimer = null;
|
|
7339
7598
|
let _ollamaLastModelNames = '';
|
|
7599
|
+
let _ollamaConfigureError = '';
|
|
7340
7600
|
let _ocpHistoryStats = null;
|
|
7341
7601
|
|
|
7342
7602
|
function formatBytes(bytes) {
|
|
@@ -7367,7 +7627,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7367
7627
|
ocpHistoryMetaEl.textContent = kit.tx('settings.redesign.opencode.historyWipeInProgress');
|
|
7368
7628
|
return;
|
|
7369
7629
|
}
|
|
7370
|
-
const totalBytes =
|
|
7630
|
+
const totalBytes = Number(data.databaseBytes) || 0;
|
|
7371
7631
|
ocpHistoryMetaEl.textContent = kit.tx('settings.redesign.opencode.stored', { formatCount: formatCount(data.sessions, 'session'), formatBytes: formatBytes(totalBytes) });
|
|
7372
7632
|
ocpHistoryClearBtn.disabled = false;
|
|
7373
7633
|
} catch (error) {
|
|
@@ -7379,8 +7639,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7379
7639
|
if (document.querySelector('.ocp-history-clear-overlay')) return;
|
|
7380
7640
|
const confirmation = _ocpHistoryStats?.confirmationPhrase || 'DELETE OPENCODE HISTORY';
|
|
7381
7641
|
const sessionCount = Number(_ocpHistoryStats?.sessions) || 0;
|
|
7382
|
-
const totalBytes =
|
|
7383
|
-
+ (Number(_ocpHistoryStats?.artifactBytes) || 0);
|
|
7642
|
+
const totalBytes = Number(_ocpHistoryStats?.databaseBytes) || 0;
|
|
7384
7643
|
const modal = document.createElement('div');
|
|
7385
7644
|
modal.className = 'tag-delete-overlay ocp-history-clear-overlay';
|
|
7386
7645
|
modal.innerHTML = `
|
|
@@ -7394,9 +7653,9 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7394
7653
|
<p class="ocp-history-modal-subtitle">${formatCount(sessionCount, 'session')} ${kit.th('settings.redesign.opencode.currentlyStored', { formatBytes: formatBytes(totalBytes) })}</p>
|
|
7395
7654
|
</div>
|
|
7396
7655
|
</div>
|
|
7397
|
-
<div class="ocp-history-warning"><strong>${kit.te('settings.redesign.opencode.thisCannotBeUndone')}</strong> ${kit.te('settings.redesign.opencode.
|
|
7656
|
+
<div class="ocp-history-warning"><strong>${kit.te('settings.redesign.opencode.thisCannotBeUndone')}</strong> ${kit.te('settings.redesign.opencode.historyClearSteps')}</div>
|
|
7398
7657
|
<ul class="ocp-history-scope">
|
|
7399
|
-
<li>${kit.te('settings.redesign.opencode.
|
|
7658
|
+
<li>${kit.te('settings.redesign.opencode.historyClearDeletes')}</li>
|
|
7400
7659
|
<li>${kit.te('settings.redesign.opencode.deletesSynabunSDerivedOpencodeResume')}</li>
|
|
7401
7660
|
<li>${kit.te('settings.redesign.opencode.keepsProviderCredentialsAccountConfigSet')}</li>
|
|
7402
7661
|
</ul>
|
|
@@ -7444,7 +7703,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7444
7703
|
confirmButton.disabled = true;
|
|
7445
7704
|
confirmButton.textContent = kit.tx('settings.redesign.opencode.clearing');
|
|
7446
7705
|
feedback.dataset.state = 'working';
|
|
7447
|
-
feedback.textContent = kit.tx('settings.redesign.opencode.
|
|
7706
|
+
feedback.textContent = kit.tx('settings.redesign.opencode.historyClearWorking');
|
|
7448
7707
|
const requestId = crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`;
|
|
7449
7708
|
sessionStorage.setItem('synabun-opencode-history-clear-request', requestId);
|
|
7450
7709
|
try {
|
|
@@ -7457,9 +7716,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7457
7716
|
if (!response.ok || !data.ok) throw new Error(data.error || kit.tx('settings.redesign.opencode.historyWipeFailedHttp', { status: response.status }));
|
|
7458
7717
|
|
|
7459
7718
|
const result = data.result || {};
|
|
7460
|
-
const reclaimed = Math.max(0,
|
|
7461
|
-
(Number(result.databaseBytesBefore) || 0) - (Number(result.databaseBytesAfter) || 0)
|
|
7462
|
-
+ (Number(result.artifactBytesDeleted) || 0));
|
|
7719
|
+
const reclaimed = Math.max(0, (Number(result.databaseBytesBefore) || 0) - (Number(result.databaseBytesAfter) || 0));
|
|
7463
7720
|
const warnings = Array.isArray(result.warnings) ? result.warnings.filter(Boolean) : [];
|
|
7464
7721
|
feedback.dataset.state = warnings.length ? 'warning' : 'working';
|
|
7465
7722
|
feedback.textContent = warnings.length
|
|
@@ -7500,7 +7757,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7500
7757
|
dotColor = '#facc15';
|
|
7501
7758
|
statusText = kit.tx('settings.redesign.opencode.configuring');
|
|
7502
7759
|
statusCls = 'stg-provider-status-key';
|
|
7503
|
-
bodyHtml = '<div class="stg-provider-note">' + kit.te('settings.redesign.opencode.
|
|
7760
|
+
bodyHtml = '<div class="stg-provider-note">' + kit.te('settings.redesign.opencode.writingProviderConfig') + '</div>';
|
|
7504
7761
|
} else if (!_ollamaRunning) {
|
|
7505
7762
|
dotColor = 'rgba(248,113,113,0.7)';
|
|
7506
7763
|
statusText = kit.tx('settings.redesign.opencode.notDetected');
|
|
@@ -7511,6 +7768,11 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7511
7768
|
statusText = kit.tx('settings.redesign.opencode.runningVersion', { version: _ollamaVersion ? ' v' + _ollamaVersion : '' });
|
|
7512
7769
|
statusCls = 'stg-provider-status-key';
|
|
7513
7770
|
bodyHtml = '<div class="stg-provider-note">' + kit.te('settings.redesign.opencode.noModelsRun') + ' <code class="stg-code">ollama pull <model></code></div>';
|
|
7771
|
+
} else if (_ollamaConfigureError) {
|
|
7772
|
+
dotColor = 'rgba(248,113,113,0.7)';
|
|
7773
|
+
statusText = kit.tx('settings.redesign.opencode.runningVersion', { version: _ollamaVersion ? ' v' + _ollamaVersion : '' });
|
|
7774
|
+
statusCls = 'stg-provider-status-disconnected';
|
|
7775
|
+
bodyHtml = '<div class="stg-provider-note">' + esc(kit.tx('settings.redesign.opencode.error2', { message: _ollamaConfigureError })) + '</div>';
|
|
7514
7776
|
} else if (_ollamaConfigured) {
|
|
7515
7777
|
dotColor = '#4ade80';
|
|
7516
7778
|
statusText = kit.tx('settings.redesign.opencode.runningVersion', { version: _ollamaVersion ? ' v' + _ollamaVersion : '' });
|
|
@@ -7546,27 +7808,16 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7546
7808
|
const btn = existing.querySelector('.stg-ollama-remove');
|
|
7547
7809
|
if (btn) { btn.disabled = true; btn.textContent = kit.tx('settings.redesign.opencode.removing'); }
|
|
7548
7810
|
try {
|
|
7549
|
-
await fetch('/api/ollama/configure', { method: 'DELETE' });
|
|
7811
|
+
const resp = await fetch('/api/ollama/configure', { method: 'DELETE' });
|
|
7812
|
+
const data = await resp.json().catch(() => ({}));
|
|
7813
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
7550
7814
|
_ollamaConfigured = false;
|
|
7551
7815
|
_ollamaConfiguredCount = 0;
|
|
7552
7816
|
_ollamaUserRemoved = true;
|
|
7553
7817
|
renderOllamaCard();
|
|
7554
|
-
showCCToast(kit.tx('settings.redesign.opencode.
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
const pollId = setInterval(async () => {
|
|
7558
|
-
polls++;
|
|
7559
|
-
if (polls > 8) { clearInterval(pollId); return; }
|
|
7560
|
-
try {
|
|
7561
|
-
const sr = await fetch('/api/opencode/status');
|
|
7562
|
-
const sd = await sr.json();
|
|
7563
|
-
if (sd.running) {
|
|
7564
|
-
clearInterval(pollId);
|
|
7565
|
-
await refreshOcpProviders();
|
|
7566
|
-
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
7567
|
-
}
|
|
7568
|
-
} catch {}
|
|
7569
|
-
}, 2000);
|
|
7818
|
+
showCCToast(kit.tx('settings.redesign.opencode.ollamaProviderRemoved'));
|
|
7819
|
+
await refreshOcpProviders();
|
|
7820
|
+
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
7570
7821
|
} catch (e) {
|
|
7571
7822
|
showCCToast(kit.tx('settings.redesign.opencode.error2', { message: e.message }));
|
|
7572
7823
|
}
|
|
@@ -7600,7 +7851,9 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7600
7851
|
|
|
7601
7852
|
// Auto-configure if: running + has models + not configured + not user-removed
|
|
7602
7853
|
// Also re-configure if models changed while already configured
|
|
7603
|
-
|
|
7854
|
+
// A refusal (an opencode.json SynaBun cannot change now) is asked again only when the models change.
|
|
7855
|
+
if (modelsChanged) _ollamaConfigureError = '';
|
|
7856
|
+
const shouldConfigure = _ollamaRunning && _ollamaModels.length > 0 && !_ollamaUserRemoved && !_ollamaConfigureError
|
|
7604
7857
|
&& (!_ollamaConfigured || modelsChanged);
|
|
7605
7858
|
|
|
7606
7859
|
if (shouldConfigure && !_ollamaConfiguring) {
|
|
@@ -7608,31 +7861,16 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7608
7861
|
renderOllamaCard();
|
|
7609
7862
|
try {
|
|
7610
7863
|
const cfgResp = await fetch('/api/ollama/configure', { method: 'POST' });
|
|
7611
|
-
const cfgData = await cfgResp.json();
|
|
7612
|
-
|
|
7864
|
+
const cfgData = await cfgResp.json().catch(() => ({}));
|
|
7865
|
+
_ollamaConfiguring = false;
|
|
7866
|
+
if (cfgResp.ok && cfgData.ok) {
|
|
7867
|
+
// OpenCode reads the provider at once: its models are listed now.
|
|
7613
7868
|
_ollamaConfigured = true;
|
|
7614
7869
|
_ollamaConfiguredCount = cfgData.modelsConfigured;
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
let polls = 0;
|
|
7618
|
-
const pollId = setInterval(async () => {
|
|
7619
|
-
polls++;
|
|
7620
|
-
if (polls > 8) { clearInterval(pollId); _ollamaConfiguring = false; renderOllamaCard(); return; }
|
|
7621
|
-
try {
|
|
7622
|
-
const sr = await fetch('/api/opencode/status');
|
|
7623
|
-
const sd = await sr.json();
|
|
7624
|
-
if (sd.running) {
|
|
7625
|
-
clearInterval(pollId);
|
|
7626
|
-
_ollamaConfiguring = false;
|
|
7627
|
-
await refreshOcpProviders();
|
|
7628
|
-
renderOllamaCard();
|
|
7629
|
-
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
7630
|
-
}
|
|
7631
|
-
} catch {}
|
|
7632
|
-
}, 2000);
|
|
7633
|
-
}, 1000);
|
|
7870
|
+
await refreshOcpProviders();
|
|
7871
|
+
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
7634
7872
|
} else {
|
|
7635
|
-
|
|
7873
|
+
_ollamaConfigureError = cfgData.error || `HTTP ${cfgResp.status}`;
|
|
7636
7874
|
}
|
|
7637
7875
|
} catch {
|
|
7638
7876
|
_ollamaConfiguring = false;
|
|
@@ -7884,7 +8122,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7884
8122
|
}
|
|
7885
8123
|
for (const om of oauthMethods) {
|
|
7886
8124
|
const label = om.label || om.name || kit.tx('settings.redesign.opencode.reconnectProvider', { name });
|
|
7887
|
-
btns.push(`<button type="button" class="stg-action-btn compact stg-text-btn stg-ocp-oauth-btn" data-provider="${esc(id)}" data-method="${om._idx}">${esc(label)}</button>`);
|
|
8125
|
+
btns.push(`<button type="button" class="stg-action-btn compact stg-text-btn stg-ocp-oauth-btn" data-provider="${esc(id)}" data-method="${om._idx}" data-method-id="${esc(om.id || '')}">${esc(label)}</button>`);
|
|
7888
8126
|
}
|
|
7889
8127
|
authHtml = btns.join('');
|
|
7890
8128
|
removeHtml = `<button type="button" class="stg-action-btn compact stg-text-btn stg-text-btn-danger stg-ocp-remove-btn" data-provider="${esc(id)}" data-env="${esc(primaryEnv)}">${kit.te('settings.redesign.opencode.removeConfirm')}</button>`;
|
|
@@ -7898,7 +8136,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7898
8136
|
}
|
|
7899
8137
|
for (const om of oauthMethods) {
|
|
7900
8138
|
const label = om.label || om.name || kit.tx('settings.redesign.opencode.loginWith', { name: name });
|
|
7901
|
-
btns.push(`<button type="button" class="stg-action-btn compact stg-ocp-oauth-btn" data-provider="${esc(id)}" data-method="${om._idx}">${esc(label)}</button>`);
|
|
8139
|
+
btns.push(`<button type="button" class="stg-action-btn compact stg-ocp-oauth-btn" data-provider="${esc(id)}" data-method="${om._idx}" data-method-id="${esc(om.id || '')}">${esc(label)}</button>`);
|
|
7902
8140
|
}
|
|
7903
8141
|
authHtml = btns.join('');
|
|
7904
8142
|
}
|
|
@@ -7914,9 +8152,16 @@ async function openSettingsModalNow(options = {}) {
|
|
|
7914
8152
|
</div>`;
|
|
7915
8153
|
|
|
7916
8154
|
// OAuth wait indicator (hidden via inline style — JS removes it to show)
|
|
8155
|
+
// OAuth wait (hidden via inline style — JS removes it to show): OpenCode's instructions (a device code),
|
|
8156
|
+
// and, for a sign-in that ends with a code the provider shows, the field it is pasted in.
|
|
7917
8157
|
const oauthWaitHtml = `<div class="stg-ocp-oauth-wait" data-provider="${esc(id)}" style="display:none">
|
|
7918
|
-
|
|
8158
|
+
<span class="stg-ocp-oauth-status">${kit.te('settings.redesign.opencode.waitingForAuthorization')}</span>
|
|
7919
8159
|
<span class="stg-ocp-oauth-code" style="display:none"></span>
|
|
8160
|
+
<div class="stg-ocp-key-row stg-ocp-oauth-code-row" style="display:none">
|
|
8161
|
+
<input type="text" class="stg-input stg-input-sm stg-ocp-oauth-code-input" placeholder="${kit.te('settings.redesign.opencode.oauthCodePlaceholder')}" aria-label="${kit.te('settings.redesign.opencode.oauthCodeFor', { name })}" autocomplete="off" spellcheck="false" style="flex:1">
|
|
8162
|
+
<button type="button" class="stg-action-btn compact stg-ocp-oauth-code-submit">${kit.te('settings.redesign.opencode.oauthConnect')}</button>
|
|
8163
|
+
</div>
|
|
8164
|
+
<div class="stg-ocp-key-error"></div>
|
|
7920
8165
|
</div>`;
|
|
7921
8166
|
|
|
7922
8167
|
const hiddenSet = getHiddenModels();
|
|
@@ -8112,28 +8357,15 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8112
8357
|
try {
|
|
8113
8358
|
const resp = await fetch(`/api/opencode/auth/${encodeURIComponent(provId)}`, {
|
|
8114
8359
|
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
|
8115
|
-
body: JSON.stringify({
|
|
8360
|
+
body: JSON.stringify({ key }),
|
|
8116
8361
|
});
|
|
8117
|
-
const data = await resp.json();
|
|
8362
|
+
const data = await resp.json().catch(() => ({}));
|
|
8118
8363
|
if (resp.ok && data.ok !== false) {
|
|
8119
8364
|
form.style.display = 'none';
|
|
8120
8365
|
input.value = '';
|
|
8121
|
-
showCCToast(kit.tx('settings.redesign.opencode.
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
const pollId = setInterval(async () => {
|
|
8125
|
-
polls++;
|
|
8126
|
-
if (polls > 8) { clearInterval(pollId); return; }
|
|
8127
|
-
try {
|
|
8128
|
-
const sr = await fetch('/api/opencode/status');
|
|
8129
|
-
const sd = await sr.json();
|
|
8130
|
-
if (sd.running) {
|
|
8131
|
-
clearInterval(pollId);
|
|
8132
|
-
await refreshOcpProviders();
|
|
8133
|
-
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8134
|
-
}
|
|
8135
|
-
} catch {}
|
|
8136
|
-
}, 2000);
|
|
8366
|
+
showCCToast(kit.tx('settings.redesign.opencode.apiKeySaved', { provId: provId }));
|
|
8367
|
+
await refreshOcpProviders();
|
|
8368
|
+
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8137
8369
|
} else {
|
|
8138
8370
|
const msg = data.error || kit.tx('settings.redesign.opencode.failedToSaveKey');
|
|
8139
8371
|
if (errEl) { errEl.textContent = msg; errEl.style.display = ''; }
|
|
@@ -8166,24 +8398,11 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8166
8398
|
method: 'DELETE',
|
|
8167
8399
|
headers: { 'Content-Type': 'application/json' },
|
|
8168
8400
|
});
|
|
8169
|
-
const data = await resp.json();
|
|
8401
|
+
const data = await resp.json().catch(() => ({}));
|
|
8170
8402
|
if (resp.ok && data.ok !== false) {
|
|
8171
|
-
showCCToast(kit.tx('settings.redesign.opencode.
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
const pollId = setInterval(async () => {
|
|
8175
|
-
polls++;
|
|
8176
|
-
if (polls > 8) { clearInterval(pollId); return; }
|
|
8177
|
-
try {
|
|
8178
|
-
const sr = await fetch('/api/opencode/status');
|
|
8179
|
-
const sd = await sr.json();
|
|
8180
|
-
if (sd.running) {
|
|
8181
|
-
clearInterval(pollId);
|
|
8182
|
-
await refreshOcpProviders();
|
|
8183
|
-
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8184
|
-
}
|
|
8185
|
-
} catch {}
|
|
8186
|
-
}, 2000);
|
|
8403
|
+
showCCToast(kit.tx('settings.redesign.opencode.apiKeyRemoved', { provId: provId }));
|
|
8404
|
+
await refreshOcpProviders();
|
|
8405
|
+
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8187
8406
|
} else {
|
|
8188
8407
|
showCCToast(data.error || kit.tx('settings.redesign.opencode.failedToRemoveKey'));
|
|
8189
8408
|
}
|
|
@@ -8200,43 +8419,22 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8200
8419
|
btn.addEventListener('click', async () => {
|
|
8201
8420
|
const provId = btn.dataset.provider;
|
|
8202
8421
|
const methodIdx = parseInt(btn.dataset.method, 10);
|
|
8422
|
+
const card = btn.closest('.stg-provider-card');
|
|
8423
|
+
const providerName = card?.querySelector('.stg-provider-name')?.textContent?.trim() || provId;
|
|
8203
8424
|
btn.disabled = true; btn.textContent = kit.tx('settings.redesign.opencode.connecting');
|
|
8204
|
-
const waitEl = ocpProvidersEl.querySelector(`.stg-ocp-oauth-wait[data-provider="${provId}"]`);
|
|
8205
|
-
const codeEl = waitEl?.querySelector('.stg-ocp-oauth-code');
|
|
8206
8425
|
try {
|
|
8207
8426
|
const resp = await fetch(`/api/opencode/oauth/${encodeURIComponent(provId)}/authorize`, {
|
|
8208
8427
|
method: 'POST',
|
|
8209
8428
|
headers: { 'Content-Type': 'application/json' },
|
|
8210
|
-
body: JSON.stringify({ method: isNaN(methodIdx) ? 0 : methodIdx }),
|
|
8429
|
+
body: JSON.stringify({ methodID: btn.dataset.methodId || undefined, method: isNaN(methodIdx) ? 0 : methodIdx }),
|
|
8211
8430
|
});
|
|
8212
|
-
const data = await resp.json();
|
|
8213
|
-
const
|
|
8214
|
-
if (
|
|
8215
|
-
window.open(
|
|
8216
|
-
|
|
8217
|
-
if (authData.code && codeEl) { codeEl.textContent = authData.code; codeEl.style.display = ''; }
|
|
8218
|
-
if (authData.instructions) showCCToast(authData.instructions);
|
|
8219
|
-
// Poll for completion
|
|
8220
|
-
let attempts = 0;
|
|
8221
|
-
const poll = setInterval(async () => {
|
|
8222
|
-
attempts++;
|
|
8223
|
-
if (attempts > 60) { clearInterval(poll); if (waitEl) waitEl.style.display = 'none'; return; }
|
|
8224
|
-
try {
|
|
8225
|
-
// Only `connected` is read: the compact view (connected providers), not the whole catalog every 2 s.
|
|
8226
|
-
const pr = await fetch('/api/opencode/providers/full?view=compact');
|
|
8227
|
-
const pd = await pr.json();
|
|
8228
|
-
const connected = pd.data?.connected || [];
|
|
8229
|
-
if (connected.includes(provId)) {
|
|
8230
|
-
clearInterval(poll);
|
|
8231
|
-
if (waitEl) waitEl.style.display = 'none';
|
|
8232
|
-
showCCToast(kit.tx('settings.redesign.opencode.connected', { provId: provId }));
|
|
8233
|
-
await refreshOcpProviders();
|
|
8234
|
-
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8235
|
-
}
|
|
8236
|
-
} catch {}
|
|
8237
|
-
}, 2000);
|
|
8431
|
+
const data = await resp.json().catch(() => ({}));
|
|
8432
|
+
const auth = data.data || {};
|
|
8433
|
+
if (resp.ok && data.ok !== false && auth.url) {
|
|
8434
|
+
window.open(auth.url, '_blank', 'noopener');
|
|
8435
|
+
showOcpOAuthWait(provId, providerName, auth);
|
|
8238
8436
|
} else {
|
|
8239
|
-
showCCToast(
|
|
8437
|
+
showCCToast(data.error || kit.tx('settings.redesign.opencode.oauthFailedNoAuthorizationUrlReturned'));
|
|
8240
8438
|
}
|
|
8241
8439
|
} catch (e) {
|
|
8242
8440
|
showCCToast(kit.tx('settings.redesign.opencode.oauthError', { message: e.message }));
|
|
@@ -8246,6 +8444,81 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8246
8444
|
});
|
|
8247
8445
|
}
|
|
8248
8446
|
|
|
8447
|
+
// ── A sign-in under way (OpenCode 2's attempt: `auto` finishes by itself, `code` with the code pasted) ──
|
|
8448
|
+
const _ocpOAuthPolls = new Map(); // provider id → timer
|
|
8449
|
+
function endOcpOAuth(provId) {
|
|
8450
|
+
clearInterval(_ocpOAuthPolls.get(provId));
|
|
8451
|
+
_ocpOAuthPolls.delete(provId);
|
|
8452
|
+
const waitEl = ocpProvidersEl?.querySelector(`.stg-ocp-oauth-wait[data-provider="${CSS.escape(provId)}"]`);
|
|
8453
|
+
if (waitEl) waitEl.style.display = 'none';
|
|
8454
|
+
}
|
|
8455
|
+
async function finishOcpOAuth(provId) {
|
|
8456
|
+
endOcpOAuth(provId);
|
|
8457
|
+
showCCToast(kit.tx('settings.redesign.opencode.connected', { provId: provId }));
|
|
8458
|
+
await refreshOcpProviders();
|
|
8459
|
+
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8460
|
+
}
|
|
8461
|
+
function showOcpOAuthWait(provId, providerName, auth) {
|
|
8462
|
+
endOcpOAuth(provId);
|
|
8463
|
+
const waitEl = ocpProvidersEl?.querySelector(`.stg-ocp-oauth-wait[data-provider="${CSS.escape(provId)}"]`);
|
|
8464
|
+
const codeEl = waitEl?.querySelector('.stg-ocp-oauth-code');
|
|
8465
|
+
const codeRow = waitEl?.querySelector('.stg-ocp-oauth-code-row');
|
|
8466
|
+
const errEl = waitEl?.querySelector('.stg-ocp-key-error');
|
|
8467
|
+
if (waitEl) waitEl.style.display = '';
|
|
8468
|
+
if (codeEl) { codeEl.textContent = auth.instructions || ''; codeEl.style.display = auth.instructions ? '' : 'none'; }
|
|
8469
|
+
if (errEl) errEl.textContent = '';
|
|
8470
|
+
if (auth.method === 'code') {
|
|
8471
|
+
if (!codeRow) return;
|
|
8472
|
+
codeRow.style.display = '';
|
|
8473
|
+
const input = codeRow.querySelector('.stg-ocp-oauth-code-input');
|
|
8474
|
+
const submit = codeRow.querySelector('.stg-ocp-oauth-code-submit');
|
|
8475
|
+
input?.focus();
|
|
8476
|
+
const send = async () => {
|
|
8477
|
+
const code = input?.value?.trim();
|
|
8478
|
+
if (!code) return;
|
|
8479
|
+
submit.disabled = true;
|
|
8480
|
+
try {
|
|
8481
|
+
const resp = await fetch(`/api/opencode/oauth/${encodeURIComponent(provId)}/callback`, {
|
|
8482
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
8483
|
+
body: JSON.stringify({ attemptID: auth.attemptID, code }),
|
|
8484
|
+
});
|
|
8485
|
+
const data = await resp.json().catch(() => ({}));
|
|
8486
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
8487
|
+
input.value = '';
|
|
8488
|
+
await finishOcpOAuth(provId);
|
|
8489
|
+
} catch (e) {
|
|
8490
|
+
if (errEl) errEl.textContent = kit.tx('settings.redesign.opencode.oauthFailed', { message: e.message });
|
|
8491
|
+
} finally {
|
|
8492
|
+
submit.disabled = false;
|
|
8493
|
+
}
|
|
8494
|
+
};
|
|
8495
|
+
if (submit) submit.onclick = send;
|
|
8496
|
+
if (input) input.onkeydown = (event) => { if (event.key === 'Enter') { event.preventDefault(); send(); } };
|
|
8497
|
+
return;
|
|
8498
|
+
}
|
|
8499
|
+
if (codeRow) codeRow.style.display = 'none';
|
|
8500
|
+
if (!auth.attemptID) return;
|
|
8501
|
+
// Every 2 s for five minutes: then the attempt is let go (OpenCode's own attempt ends by itself).
|
|
8502
|
+
let attempts = 0;
|
|
8503
|
+
_ocpOAuthPolls.set(provId, setInterval(async () => {
|
|
8504
|
+
attempts++;
|
|
8505
|
+
if (attempts > 150) { endOcpOAuth(provId); showCCToast(kit.tx('settings.redesign.opencode.oauthGaveUp')); return; }
|
|
8506
|
+
try {
|
|
8507
|
+
const resp = await fetch(`/api/opencode/oauth/${encodeURIComponent(provId)}/status?attemptID=${encodeURIComponent(auth.attemptID)}`);
|
|
8508
|
+
const data = await resp.json().catch(() => ({}));
|
|
8509
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
8510
|
+
const status = data.data?.status;
|
|
8511
|
+
if (status === 'complete') await finishOcpOAuth(provId);
|
|
8512
|
+
else if (status === 'expired') { endOcpOAuth(provId); showCCToast(kit.tx('settings.redesign.opencode.oauthExpired')); }
|
|
8513
|
+
else if (status === 'failed') { endOcpOAuth(provId); showCCToast(kit.tx('settings.redesign.opencode.oauthFailed', { message: data.data?.message || status })); }
|
|
8514
|
+
} catch (e) {
|
|
8515
|
+
endOcpOAuth(provId);
|
|
8516
|
+
showCCToast(kit.tx('settings.redesign.opencode.oauthFailed', { message: e.message }));
|
|
8517
|
+
}
|
|
8518
|
+
}, 2000));
|
|
8519
|
+
}
|
|
8520
|
+
panelLife.signal.addEventListener('abort', () => { for (const provId of [..._ocpOAuthPolls.keys()]) endOcpOAuth(provId); });
|
|
8521
|
+
|
|
8249
8522
|
// ── Delegated card click-to-expand (survives re-renders) ──
|
|
8250
8523
|
if (ocpProvidersEl) {
|
|
8251
8524
|
ocpProvidersEl.addEventListener('click', (e) => {
|
|
@@ -8263,64 +8536,60 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8263
8536
|
}
|
|
8264
8537
|
|
|
8265
8538
|
// ── Tools/Permissions rendering ──
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
const
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8539
|
+
// What each of OpenCode's own tools may do: the rules of OpenCode's global opencode.json
|
|
8540
|
+
// (GET /api/opencode/config: `actions`, `tools`), read and written there whether or not OpenCode runs.
|
|
8541
|
+
const OCP_EFFECTS = ['allow', 'ask', 'deny'];
|
|
8542
|
+
if (ocpToolsEl) {
|
|
8543
|
+
wireDropdowns(ocpToolsEl, { signal: panelLife.signal });
|
|
8544
|
+
ocpToolsEl.addEventListener('change', async (event) => {
|
|
8545
|
+
const input = event.target;
|
|
8546
|
+
const action = input?.dataset?.tool;
|
|
8547
|
+
if (!action || !OCP_EFFECTS.includes(input.value)) return;
|
|
8548
|
+
try {
|
|
8549
|
+
const resp = await fetch('/api/opencode/config', {
|
|
8550
|
+
method: 'PATCH', headers: { 'Content-Type': 'application/json' },
|
|
8551
|
+
body: JSON.stringify({ tools: { [action]: input.value } }),
|
|
8552
|
+
});
|
|
8553
|
+
const data = await resp.json().catch(() => ({}));
|
|
8554
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
8555
|
+
applyOcpConfig(data.data);
|
|
8556
|
+
} catch (err) {
|
|
8557
|
+
showCCToast(kit.tx('settings.redesign.opencode.saveFailed', { message: err.message }));
|
|
8558
|
+
renderToolPermissions();
|
|
8283
8559
|
}
|
|
8560
|
+
}, { signal: panelLife.signal });
|
|
8561
|
+
}
|
|
8284
8562
|
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8563
|
+
function renderToolPermissionsFrom(view) {
|
|
8564
|
+
if (!ocpToolsEl) return;
|
|
8565
|
+
const actions = Array.isArray(view?.actions) ? view.actions : [];
|
|
8566
|
+
if (!actions.length) {
|
|
8567
|
+
ocpToolsEl.innerHTML = '<div class="settings-hint" style="opacity:0.4">' + kit.te('settings.redesign.opencode.noToolsAvailable') + '</div>';
|
|
8568
|
+
return;
|
|
8569
|
+
}
|
|
8570
|
+
const tools = view.tools || {};
|
|
8571
|
+
const labels = { allow: kit.tx('settings.redesign.opencode.allow'), ask: kit.tx('settings.redesign.opencode.ask'), deny: kit.tx('settings.redesign.opencode.deny') };
|
|
8572
|
+
ocpToolsEl.innerHTML = `<div class="stg-tool-grid">${actions.map((action) => {
|
|
8573
|
+
const current = OCP_EFFECTS.includes(tools[action]) ? tools[action] : 'allow';
|
|
8574
|
+
const id = `stg-ocp-tool-${String(action).replace(/[^a-z0-9_-]/gi, '-')}`;
|
|
8575
|
+
return `<div class="stg-tool-row">
|
|
8576
|
+
<span class="stg-tool-name">${esc(action)}</span>
|
|
8577
|
+
${dropdownHTML(id, OCP_EFFECTS.map((effect) => ({ value: effect, label: labels[effect], selected: effect === current })), {
|
|
8578
|
+
classes: 'stg-dropdown stg-dropdown-inline stg-ocp-tool-perm',
|
|
8579
|
+
label: kit.tx('settings.redesign.opencode.permissionFor', { tool: action }),
|
|
8580
|
+
inputAttrs: `data-tool="${esc(action)}"`,
|
|
8581
|
+
})}
|
|
8298
8582
|
</div>`;
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
if (val === 'deny') { newTools[tid] = false; delete newPerms[tid]; }
|
|
8310
|
-
else if (val === 'ask') { delete newTools[tid]; newPerms[tid] = 'ask'; }
|
|
8311
|
-
else { delete newTools[tid]; delete newPerms[tid]; }
|
|
8312
|
-
patchBody.permission = newPerms;
|
|
8313
|
-
patchBody.tools = newTools;
|
|
8314
|
-
try {
|
|
8315
|
-
await fetch('/api/opencode/config', {
|
|
8316
|
-
method: 'PATCH', headers: { 'Content-Type': 'application/json' },
|
|
8317
|
-
body: JSON.stringify(patchBody),
|
|
8318
|
-
});
|
|
8319
|
-
_ocpConfig.permission = newPerms;
|
|
8320
|
-
_ocpConfig.tools = newTools;
|
|
8321
|
-
} catch {}
|
|
8322
|
-
});
|
|
8323
|
-
});
|
|
8583
|
+
}).join('')}</div>`;
|
|
8584
|
+
}
|
|
8585
|
+
|
|
8586
|
+
async function renderToolPermissions() {
|
|
8587
|
+
if (!ocpToolsEl) return;
|
|
8588
|
+
try {
|
|
8589
|
+
const resp = await fetch('/api/opencode/config');
|
|
8590
|
+
const data = await resp.json();
|
|
8591
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
8592
|
+
applyOcpConfig(data.data);
|
|
8324
8593
|
} catch {
|
|
8325
8594
|
ocpToolsEl.innerHTML = '<div class="settings-hint" style="opacity:0.4">' + kit.te('settings.redesign.opencode.failedToLoadTools') + '</div>';
|
|
8326
8595
|
}
|
|
@@ -8338,155 +8607,37 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8338
8607
|
return null;
|
|
8339
8608
|
}
|
|
8340
8609
|
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
const
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
const isActive = name === active;
|
|
8359
|
-
thead += `<th class="stg-mx-profile-th${isActive ? ' active' : ''}" data-profile="${esc(name)}">
|
|
8360
|
-
<div class="stg-mx-profile-head" data-profile="${esc(name)}">
|
|
8361
|
-
<span class="stg-mx-profile-dot${isActive ? ' on' : ''}"></span>
|
|
8362
|
-
<span class="stg-mx-profile-name">${esc(prof.label || name)}</span>
|
|
8363
|
-
${isActive ? '' : `<button class="stg-mx-profile-del" data-profile="${esc(name)}" title="${kit.te('settings.redesign.mcp.deleteProfile')}">\u00d7</button>`}
|
|
8364
|
-
</div>
|
|
8365
|
-
</th>`;
|
|
8366
|
-
}
|
|
8367
|
-
thead += '<th class="stg-mx-add-th"><button class="stg-mx-add-btn" title="' + kit.te('settings.redesign.mcp.newProfile') + '">+</button></th></tr>';
|
|
8368
|
-
|
|
8369
|
-
// Build table body — switchable groups
|
|
8370
|
-
let tbody = '';
|
|
8371
|
-
for (const [key, info] of switchable) {
|
|
8372
|
-
tbody += `<tr>`;
|
|
8373
|
-
tbody += `<td class="stg-mx-label-td"><div class="stg-mx-label-inner">
|
|
8374
|
-
<span class="stg-mx-grp-name">${esc(info.label)}</span>
|
|
8375
|
-
<span class="stg-mx-grp-count">${info.tools}</span>
|
|
8376
|
-
</div></td>`;
|
|
8377
|
-
for (const [profName, prof] of profEntries) {
|
|
8378
|
-
const isOn = (prof.groups || []).includes(key);
|
|
8379
|
-
tbody += `<td class="stg-mx-cell" data-profile="${esc(profName)}" data-group="${esc(key)}" data-kind="group">
|
|
8380
|
-
<span class="stg-mx-toggle${isOn ? ' on' : ''}"></span>
|
|
8381
|
-
</td>`;
|
|
8382
|
-
}
|
|
8383
|
-
tbody += '<td class="stg-mx-add-spacer"></td></tr>';
|
|
8384
|
-
}
|
|
8385
|
-
|
|
8386
|
-
wrap.innerHTML = `<div class="stg-mcp-matrix-scroll"><table id="stg-mcp-matrix"><thead>${thead}</thead><tbody>${tbody}</tbody></table></div>`;
|
|
8387
|
-
|
|
8388
|
-
// --- Event handlers ---
|
|
8389
|
-
|
|
8390
|
-
// Toggle cells
|
|
8391
|
-
wrap.querySelectorAll('.stg-mx-cell').forEach(cell => {
|
|
8392
|
-
cell.addEventListener('click', async () => {
|
|
8393
|
-
const profName = cell.dataset.profile;
|
|
8394
|
-
const prof = _mcpRegistry.profiles[profName];
|
|
8395
|
-
if (!prof) return;
|
|
8396
|
-
const kind = cell.dataset.kind;
|
|
8397
|
-
if (kind === 'group') {
|
|
8398
|
-
const g = cell.dataset.group;
|
|
8399
|
-
if ((prof.groups || []).includes(g)) {
|
|
8400
|
-
prof.groups = prof.groups.filter(x => x !== g);
|
|
8401
|
-
} else {
|
|
8402
|
-
prof.groups = [...(prof.groups || []), g];
|
|
8403
|
-
}
|
|
8404
|
-
} else {
|
|
8405
|
-
const s = cell.dataset.server;
|
|
8406
|
-
if ((prof.servers || []).includes(s)) {
|
|
8407
|
-
prof.servers = prof.servers.filter(x => x !== s);
|
|
8408
|
-
} else {
|
|
8409
|
-
prof.servers = [...(prof.servers || []), s];
|
|
8410
|
-
}
|
|
8411
|
-
}
|
|
8412
|
-
await fetch(`/api/mcp/registry/profiles/${encodeURIComponent(profName)}`, {
|
|
8413
|
-
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
|
8414
|
-
body: JSON.stringify(prof),
|
|
8415
|
-
});
|
|
8416
|
-
await refreshMcpTab();
|
|
8417
|
-
});
|
|
8418
|
-
});
|
|
8419
|
-
|
|
8420
|
-
// Activate profile on name click
|
|
8421
|
-
wrap.querySelectorAll('.stg-mx-profile-head').forEach(head => {
|
|
8422
|
-
head.addEventListener('click', async (e) => {
|
|
8423
|
-
if (e.target.closest('.stg-mx-profile-del')) return;
|
|
8424
|
-
const name = head.dataset.profile;
|
|
8425
|
-
if (name === _mcpRegistry.activeProfile) return;
|
|
8426
|
-
await fetch('/api/mcp/registry/activate', {
|
|
8427
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
8428
|
-
body: JSON.stringify({ profile: name }),
|
|
8429
|
-
});
|
|
8430
|
-
showCCToast(kit.tx('settings.redesign.mcp.profileSwitchedTo', { name: name }));
|
|
8431
|
-
await refreshMcpTab();
|
|
8432
|
-
});
|
|
8433
|
-
});
|
|
8434
|
-
|
|
8435
|
-
// Delete profile
|
|
8436
|
-
wrap.querySelectorAll('.stg-mx-profile-del').forEach(btn => {
|
|
8437
|
-
btn.addEventListener('click', async (e) => {
|
|
8438
|
-
e.stopPropagation();
|
|
8439
|
-
await fetch(`/api/mcp/registry/profiles/${encodeURIComponent(btn.dataset.profile)}`, { method: 'DELETE' });
|
|
8440
|
-
await refreshMcpTab();
|
|
8441
|
-
});
|
|
8610
|
+
let _settingsToolCatalog = null;
|
|
8611
|
+
const defaultToolsWrap = overlay.querySelector('#stg-mcp-default-tools');
|
|
8612
|
+
if (defaultToolsWrap) wireDropdowns(defaultToolsWrap, { signal: panelLife.signal });
|
|
8613
|
+
function renderDefaultTools() {
|
|
8614
|
+
const wrap = overlay.querySelector('#stg-mcp-default-tools');
|
|
8615
|
+
if (!wrap) return;
|
|
8616
|
+
wrap.innerHTML = _settingsToolCatalog ? toolPickerHTML('stg-default-tools', _settingsToolCatalog.defaultTools, { catalog: _settingsToolCatalog, provider: 'all', label: toolWord('defaultTools') }) : '';
|
|
8617
|
+
wrap.querySelector('#stg-default-tools')?.addEventListener('change', async (event) => {
|
|
8618
|
+
const tools = readToolPicker(event.target, { inherit: false });
|
|
8619
|
+
try {
|
|
8620
|
+
const response = await fetch('/api/mcp/tools/default', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tools }) });
|
|
8621
|
+
const result = await response.json();
|
|
8622
|
+
if (!response.ok || !result.ok) throw new Error(result.error || toolWord('saveFailed'));
|
|
8623
|
+
_settingsToolCatalog.defaultTools = result.tools;
|
|
8624
|
+
showCCToast(toolWord('saved'));
|
|
8625
|
+
} catch (error) { showCCToast(error.message || toolWord('saveFailed')); }
|
|
8626
|
+
renderDefaultTools();
|
|
8442
8627
|
});
|
|
8443
|
-
|
|
8444
|
-
// Inline add profile
|
|
8445
|
-
const addBtn = wrap.querySelector('.stg-mx-add-btn');
|
|
8446
|
-
if (addBtn) {
|
|
8447
|
-
addBtn.addEventListener('click', () => {
|
|
8448
|
-
const th = addBtn.closest('th');
|
|
8449
|
-
th.innerHTML = `<input class="stg-mx-inline-input" type="text" placeholder="${kit.te('settings.redesign.mcp.name')}" autofocus>`;
|
|
8450
|
-
const input = th.querySelector('input');
|
|
8451
|
-
input.focus();
|
|
8452
|
-
const create = async () => {
|
|
8453
|
-
const name = input.value.trim();
|
|
8454
|
-
if (!name) { await refreshMcpTab(); return; }
|
|
8455
|
-
await fetch(`/api/mcp/registry/profiles/${encodeURIComponent(name)}`, {
|
|
8456
|
-
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
|
8457
|
-
body: JSON.stringify({ label: name, groups: [], servers: [] }),
|
|
8458
|
-
});
|
|
8459
|
-
await refreshMcpTab();
|
|
8460
|
-
};
|
|
8461
|
-
input.addEventListener('keydown', (e) => {
|
|
8462
|
-
if (e.key === 'Enter') { e.preventDefault(); e.stopPropagation(); create(); }
|
|
8463
|
-
if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); refreshMcpTab(); }
|
|
8464
|
-
});
|
|
8465
|
-
input.addEventListener('blur', create);
|
|
8466
|
-
});
|
|
8467
|
-
}
|
|
8468
8628
|
}
|
|
8469
8629
|
|
|
8470
8630
|
function renderAlwaysOn() {
|
|
8471
8631
|
const el = overlay.querySelector('#stg-mcp-always-on');
|
|
8472
|
-
if (!el
|
|
8473
|
-
|
|
8474
|
-
const alwaysOn = Object.entries(tg).filter(([, info]) => info.alwaysOn);
|
|
8475
|
-
if (!alwaysOn.length) { el.innerHTML = ''; return; }
|
|
8476
|
-
el.innerHTML = `
|
|
8477
|
-
<div class="stg-mcp-section-header">${kit.te('settings.redesign.mcp.alwaysOn')}</div>
|
|
8478
|
-
<div class="stg-mx-always-on-row">
|
|
8479
|
-
${alwaysOn.map(([, info]) =>
|
|
8480
|
-
`<span class="stg-mx-always-chip">${esc(info.label)}<span class="stg-mx-grp-count">${info.tools}</span></span>`
|
|
8481
|
-
).join('')}
|
|
8482
|
-
</div>`;
|
|
8632
|
+
if (!el) return;
|
|
8633
|
+
el.innerHTML = _settingsToolCatalog ? `<div class="stg-mcp-section-header">${esc(toolWord('alwaysOn'))}</div><div class="stg-mx-always-on-row">${_settingsToolCatalog.alwaysOnTools.map(name => `<span class="stg-mx-always-chip">${esc(name)}</span>`).join('')}</div>` : '';
|
|
8483
8634
|
}
|
|
8484
8635
|
|
|
8485
8636
|
function renderExtServerList() {
|
|
8486
8637
|
const el = overlay.querySelector('#stg-mcp-ext-servers');
|
|
8487
8638
|
if (!el || !_mcpRegistry) return;
|
|
8488
|
-
const servers = _mcpRegistry.servers || {};
|
|
8489
|
-
const
|
|
8639
|
+
const servers = { ...(_mcpRegistry.servers || {}) };
|
|
8640
|
+
for (const row of _settingsToolCatalog?.servers || []) { const name = row.ref.slice(4); if (!servers[name]) servers[name] = { type: toolWord('configuredExternally'), discovered: true }; }
|
|
8490
8641
|
const entries = Object.entries(servers).filter(([, info]) => !info.builtin);
|
|
8491
8642
|
if (!entries.length) {
|
|
8492
8643
|
el.innerHTML = '<div class="stg-mcp-empty-note">' + kit.te('settings.redesign.mcp.noExternalServersConfigured') + '</div>';
|
|
@@ -8501,38 +8652,31 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8501
8652
|
<span class="stg-mx-ext-name">${esc(name)}</span>
|
|
8502
8653
|
<span class="stg-mx-ext-type">${esc(info.type || '?')}</span>
|
|
8503
8654
|
</div>
|
|
8504
|
-
<div class="stg-mx-ext-sub">${
|
|
8655
|
+
<div class="stg-mx-ext-sub">${esc(toolWord('agentAccessHelp'))}</div>
|
|
8505
8656
|
</div>
|
|
8506
|
-
|
|
8657
|
+
${info.discovered ? '' : `<button class="stg-mx-srv-del stg-mx-ext-remove" ${kit.inv('MCP004')} data-server="${esc(name)}" title="${kit.te('settings.redesign.mcp.removeServer')}">\u00d7</button>`}
|
|
8507
8658
|
</div>
|
|
8508
|
-
<div class="stg-
|
|
8509
|
-
${
|
|
8510
|
-
const isOn = (prof.servers || []).includes(name);
|
|
8511
|
-
return `<button class="stg-mx-ext-toggle${isOn ? ' on' : ''}" data-profile="${esc(profName)}" data-server="${esc(name)}" title="${isOn ? kit.te('settings.redesign.mcp.disable') : kit.te('settings.redesign.mcp.enable')} ${esc(name)} ${kit.te('settings.redesign.mcp.for')} ${esc(prof.label || profName)}">
|
|
8512
|
-
<span class="stg-mx-ext-toggle-name">${esc(prof.label || profName)}</span>
|
|
8513
|
-
</button>`;
|
|
8514
|
-
}).join('')}
|
|
8659
|
+
<div class="stg-field"><span>${esc(toolWord('agentAccess'))}</span>
|
|
8660
|
+
<button type="button" class="cc-toggle${_mcpRegistry.agentAccess?.[name] === false ? '' : ' on'}" role="switch" ${kit.inv('MCP005')} data-agent-server="${esc(name)}" aria-label="${esc(toolWord('agentAccess'))}: ${esc(name)}" aria-checked="${_mcpRegistry.agentAccess?.[name] !== false}"></button>
|
|
8515
8661
|
</div>
|
|
8516
8662
|
</div>`
|
|
8517
8663
|
).join('');
|
|
8518
8664
|
|
|
8519
|
-
el.querySelectorAll('
|
|
8520
|
-
|
|
8521
|
-
const
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
});
|
|
8535
|
-
await refreshMcpTab();
|
|
8665
|
+
el.querySelectorAll('[data-agent-server]').forEach(input => {
|
|
8666
|
+
input.addEventListener('click', async () => {
|
|
8667
|
+
const enabled = input.getAttribute('aria-checked') !== 'true';
|
|
8668
|
+
input.disabled = true;
|
|
8669
|
+
try {
|
|
8670
|
+
const response = await fetch(`/api/mcp/registry/agent-access/${encodeURIComponent(input.dataset.agentServer)}`, {
|
|
8671
|
+
method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled }),
|
|
8672
|
+
});
|
|
8673
|
+
const result = await response.json();
|
|
8674
|
+
if (!response.ok || !result.ok) throw new Error(result.error || toolWord('saveFailed'));
|
|
8675
|
+
_mcpRegistry.agentAccess = result.agentAccess;
|
|
8676
|
+
input.classList.toggle('on', enabled);
|
|
8677
|
+
input.setAttribute('aria-checked', String(enabled));
|
|
8678
|
+
} catch (error) { showCCToast(error.message || toolWord('saveFailed')); }
|
|
8679
|
+
finally { input.disabled = false; }
|
|
8536
8680
|
});
|
|
8537
8681
|
});
|
|
8538
8682
|
|
|
@@ -8575,7 +8719,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8575
8719
|
claudeCode: { label: 'CC', title: 'Claude Code (~/.claude.json)' },
|
|
8576
8720
|
opencode: { label: 'OC', title: 'OpenCode' },
|
|
8577
8721
|
codex: { label: 'CX', title: 'Codex (~/.codex/config.toml)' },
|
|
8578
|
-
|
|
8722
|
+
antigravity: { label: 'AG', title: 'Antigravity (~/.gemini/config/mcp_config.json)' },
|
|
8579
8723
|
};
|
|
8580
8724
|
|
|
8581
8725
|
async function renderInstalledAll() {
|
|
@@ -8687,7 +8831,8 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8687
8831
|
|
|
8688
8832
|
async function refreshMcpTab() {
|
|
8689
8833
|
await fetchMcpRegistry();
|
|
8690
|
-
|
|
8834
|
+
_settingsToolCatalog = await fetchToolCatalog(true);
|
|
8835
|
+
renderDefaultTools();
|
|
8691
8836
|
renderAlwaysOn();
|
|
8692
8837
|
renderExtServerList();
|
|
8693
8838
|
renderPluginsList();
|
|
@@ -8845,13 +8990,18 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8845
8990
|
const platformsEl = overlay.querySelector('#stg-mcp-platforms');
|
|
8846
8991
|
const feedbackEl = overlay.querySelector('#stg-mcp-sync-feedback');
|
|
8847
8992
|
|
|
8993
|
+
// The type is a styled dropdown: filled from code it fires no change, so the form's transport follows here too.
|
|
8994
|
+
function setServerType(type) {
|
|
8995
|
+
if (typeEl) setDropdownValue(typeEl, type);
|
|
8996
|
+
if (addForm) addForm.dataset.transport = typeEl?.value || type;
|
|
8997
|
+
}
|
|
8998
|
+
|
|
8848
8999
|
function resetAddForm() {
|
|
8849
9000
|
if (pasteZone) pasteZone.value = '';
|
|
8850
9001
|
if (nameEl) nameEl.value = '';
|
|
8851
9002
|
if (cmdEl) cmdEl.value = '';
|
|
8852
9003
|
if (urlEl) urlEl.value = '';
|
|
8853
|
-
|
|
8854
|
-
if (addForm) addForm.dataset.transport = 'stdio';
|
|
9004
|
+
setServerType('stdio');
|
|
8855
9005
|
// Clear arg chips
|
|
8856
9006
|
if (argChips) argChips.querySelectorAll('.stg-mcp-arg-chip').forEach(c => c.remove());
|
|
8857
9007
|
// Clear env + header rows
|
|
@@ -8935,10 +9085,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
8935
9085
|
function populateAddForm(parsed) {
|
|
8936
9086
|
if (!parsed) return;
|
|
8937
9087
|
if (parsed.name && nameEl) nameEl.value = parsed.name;
|
|
8938
|
-
if (parsed.type
|
|
8939
|
-
typeEl.value = parsed.type;
|
|
8940
|
-
if (addForm) addForm.dataset.transport = parsed.type;
|
|
8941
|
-
}
|
|
9088
|
+
if (parsed.type) setServerType(parsed.type);
|
|
8942
9089
|
if (parsed.command && cmdEl) cmdEl.value = parsed.command;
|
|
8943
9090
|
if (parsed.url && urlEl) urlEl.value = parsed.url;
|
|
8944
9091
|
if (parsed.args && parsed.args.length) {
|
|
@@ -9045,7 +9192,7 @@ async function openSettingsModalNow(options = {}) {
|
|
|
9045
9192
|
function applyDetected(name, detected) {
|
|
9046
9193
|
if (!detected) return;
|
|
9047
9194
|
if (nameEl && name) nameEl.value = name;
|
|
9048
|
-
|
|
9195
|
+
setServerType(detected.type || 'stdio');
|
|
9049
9196
|
if (cmdEl) cmdEl.value = detected.command || '';
|
|
9050
9197
|
// Rebuild arg chips
|
|
9051
9198
|
if (argChips && argInput) {
|
|
@@ -9199,55 +9346,62 @@ async function openSettingsModalNow(options = {}) {
|
|
|
9199
9346
|
});
|
|
9200
9347
|
|
|
9201
9348
|
// ── Config save helpers ──
|
|
9349
|
+
// PATCH /api/opencode/config (OpenCode 2's global opencode.json): the answer is the configuration as it is now.
|
|
9202
9350
|
async function saveOcpConfig(patch) {
|
|
9203
9351
|
try {
|
|
9204
|
-
await fetch('/api/opencode/config', {
|
|
9352
|
+
const resp = await fetch('/api/opencode/config', {
|
|
9205
9353
|
method: 'PATCH', headers: { 'Content-Type': 'application/json' },
|
|
9206
9354
|
body: JSON.stringify(patch),
|
|
9207
9355
|
});
|
|
9208
|
-
|
|
9356
|
+
const data = await resp.json().catch(() => ({}));
|
|
9357
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
9358
|
+
applyOcpConfig(data.data);
|
|
9359
|
+
return true;
|
|
9360
|
+
} catch (err) {
|
|
9361
|
+
showCCToast(kit.tx('settings.redesign.opencode.saveFailed', { message: err.message }));
|
|
9362
|
+
loadOcpConfig();
|
|
9363
|
+
return false;
|
|
9364
|
+
}
|
|
9209
9365
|
}
|
|
9210
9366
|
|
|
9211
9367
|
// ── Compaction handlers ──
|
|
9212
9368
|
const compactAutoEl = overlay.querySelector('#stg-ocp-compact-auto');
|
|
9213
|
-
const compactPruneEl = overlay.querySelector('#stg-ocp-compact-prune');
|
|
9214
9369
|
const compactReservedEl = overlay.querySelector('#stg-ocp-compact-reserved');
|
|
9215
9370
|
|
|
9371
|
+
// An emptied field is OpenCode's own default again (buffer: null).
|
|
9216
9372
|
function saveCompaction() {
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
reserved: Number(compactReservedEl?.value) || 10000,
|
|
9222
|
-
},
|
|
9223
|
-
});
|
|
9373
|
+
const raw = String(compactReservedEl?.value ?? '').trim();
|
|
9374
|
+
const buffer = raw === '' ? null : Math.round(Number(raw));
|
|
9375
|
+
if (buffer !== null && !(Number.isFinite(buffer) && buffer >= 0 && buffer <= 1_000_000)) { loadOcpConfig(); return; }
|
|
9376
|
+
saveOcpConfig({ compaction: { auto: compactAutoEl?.checked ?? true, buffer } });
|
|
9224
9377
|
}
|
|
9225
9378
|
compactAutoEl?.addEventListener('change', saveCompaction);
|
|
9226
|
-
compactPruneEl?.addEventListener('change', saveCompaction);
|
|
9227
9379
|
compactReservedEl?.addEventListener('change', saveCompaction);
|
|
9228
9380
|
|
|
9229
|
-
// ── Snapshot toggle (
|
|
9381
|
+
// ── Snapshot toggle (OpenCode's `snapshots`, read at once) ──
|
|
9230
9382
|
const snapshotToggle = overlay.querySelector('#stg-ocp-snapshot-toggle');
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9383
|
+
snapshotToggle?.addEventListener('click', () => {
|
|
9384
|
+
const next = !snapshotToggle.classList.contains('on');
|
|
9385
|
+
snapshotToggle.classList.toggle('on', next); // optimistic; the answer (or a failed save's reload) sets it
|
|
9386
|
+
saveOcpConfig({ snapshots: next });
|
|
9387
|
+
});
|
|
9388
|
+
|
|
9389
|
+
// The configuration as GET / PATCH /api/opencode/config give it: the file SynaBun edits, compaction,
|
|
9390
|
+
// snapshots and the tools' permissions.
|
|
9391
|
+
function applyOcpConfig(view) {
|
|
9392
|
+
if (!view || typeof view !== 'object') return;
|
|
9393
|
+
_ocpConfig = view;
|
|
9394
|
+
const comp = view.compaction || {};
|
|
9395
|
+
if (compactAutoEl) compactAutoEl.checked = comp.auto !== false;
|
|
9396
|
+
if (compactReservedEl && document.activeElement !== compactReservedEl) compactReservedEl.value = Number.isFinite(comp.buffer) ? String(comp.buffer) : '';
|
|
9397
|
+
snapshotToggle?.classList.toggle('on', view.snapshots !== false);
|
|
9398
|
+
if (ocpConfigPathEl && view.file) ocpConfigPathEl.textContent = view.file;
|
|
9399
|
+
const jsonc = (Array.isArray(view.files) ? view.files : []).find((file) => file?.exists && /\.jsonc$/i.test(String(file.path || '')));
|
|
9400
|
+
if (ocpConfigJsoncEl) {
|
|
9401
|
+
ocpConfigJsoncEl.hidden = !jsonc;
|
|
9402
|
+
ocpConfigJsoncEl.innerHTML = jsonc ? kit.th('settings.redesign.opencode.jsoncWins', { path: `<code class="stg-code">${esc(jsonc.path)}</code>` }) : '';
|
|
9403
|
+
}
|
|
9404
|
+
renderToolPermissionsFrom(view);
|
|
9251
9405
|
}
|
|
9252
9406
|
|
|
9253
9407
|
// Capability filter pills — use CSS vars, not inline color
|
|
@@ -9285,28 +9439,16 @@ async function openSettingsModalNow(options = {}) {
|
|
|
9285
9439
|
});
|
|
9286
9440
|
|
|
9287
9441
|
// ── Open config file in Synabun's editor ──
|
|
9442
|
+
// OpenCode 2's global opencode.json (made with its $schema when there is none yet).
|
|
9288
9443
|
overlay.querySelector('#stg-ocp-open-config')?.addEventListener('click', async () => {
|
|
9289
9444
|
try {
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
if (revealResp.ok && revealData?.path) {
|
|
9297
|
-
const basePath = String(revealData.path).replace(/\\/g, '/').replace(/\/+$/, '');
|
|
9298
|
-
filePath = basePath.endsWith('/config.json') ? basePath : `${basePath}/config.json`;
|
|
9299
|
-
} else {
|
|
9300
|
-
const fileResp = await fetch('/api/opencode/config/file');
|
|
9301
|
-
const fileData = await fileResp.json();
|
|
9302
|
-
if (!fileResp.ok || !fileData?.path) {
|
|
9303
|
-
showCCToast(fileData?.error || revealData?.error || kit.tx('settings.redesign.opencode.failedToOpenOpencodeConfig'));
|
|
9304
|
-
return;
|
|
9305
|
-
}
|
|
9306
|
-
filePath = fileData.path;
|
|
9445
|
+
const fileResp = await fetch('/api/opencode/config/file');
|
|
9446
|
+
const fileData = await fileResp.json().catch(() => ({}));
|
|
9447
|
+
if (!fileResp.ok || !fileData?.path) {
|
|
9448
|
+
showCCToast(fileData?.error || kit.tx('settings.redesign.opencode.failedToOpenOpencodeConfig'));
|
|
9449
|
+
return;
|
|
9307
9450
|
}
|
|
9308
|
-
|
|
9309
|
-
emit('open-file-editor', { filePath });
|
|
9451
|
+
emit('open-file-editor', { filePath: fileData.path });
|
|
9310
9452
|
} catch (err) {
|
|
9311
9453
|
console.error('[settings] Failed to open OpenCode config:', err);
|
|
9312
9454
|
showCCToast(kit.tx('settings.redesign.opencode.failedToOpenOpencodeConfig'));
|
|
@@ -9334,38 +9476,46 @@ async function openSettingsModalNow(options = {}) {
|
|
|
9334
9476
|
} catch {}
|
|
9335
9477
|
}
|
|
9336
9478
|
|
|
9337
|
-
// ── Load config values into UI ──
|
|
9479
|
+
// ── Load config values into UI (read from the files: OpenCode need not run) ──
|
|
9338
9480
|
async function loadOcpConfig() {
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9481
|
+
await renderToolPermissions();
|
|
9482
|
+
}
|
|
9483
|
+
|
|
9484
|
+
// The OpenCode SynaBun found: its version, or why SynaBun does not drive it and how to get OpenCode 2.
|
|
9485
|
+
function renderOcpInstall(installed) {
|
|
9486
|
+
const note = describeOpencodeInstall(installed, { th: kit.th, tx: kit.tx });
|
|
9487
|
+
if (ocpInstallEl) {
|
|
9488
|
+
ocpInstallEl.hidden = !note.html;
|
|
9489
|
+
ocpInstallEl.dataset.tone = note.tone;
|
|
9490
|
+
ocpInstallEl.innerHTML = note.html;
|
|
9491
|
+
}
|
|
9492
|
+
return note;
|
|
9348
9493
|
}
|
|
9349
9494
|
|
|
9350
9495
|
// ── Master refresh ──
|
|
9351
9496
|
async function refreshOcpStatus() {
|
|
9352
9497
|
setOcpStatus(kit.tx('settings.redesign.opencode.checking'), 'dim');
|
|
9498
|
+
loadOcpConfig();
|
|
9353
9499
|
try {
|
|
9354
9500
|
const resp = await fetch('/api/opencode/status');
|
|
9355
9501
|
const data = await resp.json();
|
|
9502
|
+
const note = renderOcpInstall(data.installed);
|
|
9356
9503
|
if (data.clearingHistory) {
|
|
9357
9504
|
setOcpStatus(kit.tx('settings.redesign.opencode.clearingHistory'), 'warn');
|
|
9358
9505
|
} else if (data.running) {
|
|
9359
9506
|
setOcpStatus(kit.tx('settings.redesign.opencode.runningVersion', { version: data.version ? ' v' + data.version : '' }), 'ok');
|
|
9360
|
-
await
|
|
9361
|
-
refreshOcpProviders(),
|
|
9362
|
-
loadOcpConfig(),
|
|
9363
|
-
renderToolPermissions(),
|
|
9364
|
-
]);
|
|
9507
|
+
await refreshOcpProviders();
|
|
9365
9508
|
} else {
|
|
9366
|
-
setOcpStatus(kit.tx('settings.redesign.opencode.
|
|
9509
|
+
if (note.status === 'notSupported') setOcpStatus(kit.tx('settings.redesign.opencode.notSupportedStatus'), 'err');
|
|
9510
|
+
else if (note.status === 'notInstalled') setOcpStatus(kit.tx('settings.redesign.opencode.notInstalledStatus'), 'dim');
|
|
9511
|
+
else setOcpStatus(kit.tx('settings.redesign.opencode.offline'), 'dim');
|
|
9367
9512
|
if (ocpProvidersEl) ocpProvidersEl.innerHTML = '<div class="settings-hint" style="opacity:0.4">' + kit.te('settings.redesign.opencode.startTheServerToSeeProviders') + '</div>';
|
|
9368
|
-
|
|
9513
|
+
// Something else stands in the way (another serve on the port, a serve that did not answer): the server's sentence.
|
|
9514
|
+
if (!note.html && data.problem && ocpInstallEl) {
|
|
9515
|
+
ocpInstallEl.hidden = false;
|
|
9516
|
+
ocpInstallEl.dataset.tone = 'warn';
|
|
9517
|
+
ocpInstallEl.textContent = data.problem;
|
|
9518
|
+
}
|
|
9369
9519
|
}
|
|
9370
9520
|
} catch (err) {
|
|
9371
9521
|
setOcpStatus(kit.tx('settings.redesign.opencode.error'), 'err');
|
|
@@ -9420,6 +9570,11 @@ async function openSettingsModalNow(options = {}) {
|
|
|
9420
9570
|
}
|
|
9421
9571
|
|
|
9422
9572
|
// ── Shell behaviours: jump lists, keyboard disclosure, page picker ──
|
|
9573
|
+
// The three styled dropdowns of the shell and the panes (page picker, MCP server type, YouTube upload transport).
|
|
9574
|
+
for (const id of ['stg-page-select', 'stg-mcp-srv-type', 'yt-upload-transport']) {
|
|
9575
|
+
const box = overlay.querySelector(`.cc-dropdown[data-for="${id}"]`)?.parentElement;
|
|
9576
|
+
if (box) wireDropdowns(box, { signal: panelLife.signal });
|
|
9577
|
+
}
|
|
9423
9578
|
wireSettingsShell(overlay, { activate: (pageId) => activateSettingsTab(overlay, pageId) });
|
|
9424
9579
|
// ── Search: the index is built from the strings of the active locale; what the panes load is read at query time ──
|
|
9425
9580
|
// In another locale the English names are searched too (someone who learned the settings in English still finds
|
|
@@ -9635,3 +9790,5 @@ export function initSettings() {
|
|
|
9635
9790
|
window.checkSyncStatus = checkSyncStatus;
|
|
9636
9791
|
window.copySyncPrompt = copySyncPrompt;
|
|
9637
9792
|
}
|
|
9793
|
+
|
|
9794
|
+
configureToolPickerTranslation((key, params) => kit.tx(key, params));
|