synabun 2.0.10 → 2.1.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 +86 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +22 -0
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +2 -2
- 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 +17 -8
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +1 -1
- 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 +228 -0
- package/lib/virustotal.mjs +192 -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/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/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/neural-interface.d.ts +16 -10
- 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 +20 -62
- 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/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 +10 -10
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +22 -13
- 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/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.js +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 +639 -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/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/neural-interface.ts +79 -20
- package/mcp-server/src/services/server-instructions.ts +27 -63
- 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/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +22 -14
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/skill-tools.ts +589 -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 +3463 -200
- package/neural-interface/i18n/pt-BR.json +3411 -151
- package/neural-interface/lib/assistant-api.js +127 -28
- 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-catalog.js +6 -7
- package/neural-interface/lib/assistant-dispatch.js +871 -156
- package/neural-interface/lib/assistant-envelope.js +3 -1
- package/neural-interface/lib/assistant-persona.js +80 -11
- package/neural-interface/lib/assistant-plan-permissions.js +10 -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 +13 -8
- package/neural-interface/lib/assistant-runtime.js +56 -18
- package/neural-interface/lib/assistant-task-prompt.js +78 -14
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/backup-service.js +620 -3
- 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 +83 -0
- package/neural-interface/lib/browser-network-guard.js +284 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +186 -31
- package/neural-interface/lib/claude-agent-bridge.js +4 -2
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install.js +18 -138
- package/neural-interface/lib/cli-resolver.js +4 -1
- package/neural-interface/lib/cli-update-planner.js +32 -31
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-worker-app-server.js +172 -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/exec-loop-command.js +4 -8
- 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 +0 -29
- package/neural-interface/lib/mcp-external-servers.js +323 -0
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/morelogin.js +30 -0
- package/neural-interface/lib/native-loop-providers.js +547 -47
- package/neural-interface/lib/native-loop-runtime.js +268 -41
- 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 +44 -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/peer-identity.js +763 -0
- package/neural-interface/lib/request-gate.js +17 -2
- package/neural-interface/lib/rulesets/api.js +1 -1
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +92 -27
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +17 -30
- 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 +294 -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 +267 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +564 -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 +214 -0
- package/neural-interface/lib/skills/eval-confinement.js +397 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +119 -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 +200 -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 +387 -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 +262 -0
- package/neural-interface/lib/skills/runtime-checks.js +355 -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 +522 -0
- package/neural-interface/lib/skills/telemetry.js +113 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/whatsapp/bridge.js +6 -3
- package/neural-interface/lib/worker-confinement.js +1192 -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-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +229 -0
- package/neural-interface/lib/worker-sandbox.js +299 -0
- package/neural-interface/package-lock.json +18 -12
- package/neural-interface/package.json +2 -2
- package/neural-interface/public/index.html +3 -1
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +2 -2
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +35 -19
- 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 +33 -2
- 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 +49 -35
- package/neural-interface/public/shared/assistant/asst-control.js +157 -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-limit-dock.js +40 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -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 +106 -11
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
- package/neural-interface/public/shared/assistant/asst-render.js +401 -77
- package/neural-interface/public/shared/assistant/asst-route.js +83 -12
- package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
- package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
- package/neural-interface/public/shared/assistant/asst-state.js +11 -15
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
- 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 +8 -7
- package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
- package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
- package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
- package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
- package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +11 -3
- package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-view.js +4 -8
- package/neural-interface/public/shared/cli-status.js +2 -2
- package/neural-interface/public/shared/constants.js +2 -2
- 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 +366 -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/crash-notice-view.js +168 -0
- package/neural-interface/public/shared/desktop-update-view.js +20 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- package/neural-interface/public/shared/html-shell.js +9 -11
- 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 +47 -252
- 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-render.js +6 -21
- 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 +0 -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 +14 -47
- package/neural-interface/public/shared/page-session.js +96 -0
- 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-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/skills/ss-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +219 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +130 -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 +141 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +475 -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 +236 -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 +229 -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-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 +279 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +681 -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 +248 -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 +425 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +1525 -1779
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- 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/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-automation-studio.js +91 -128
- package/neural-interface/public/shared/ui-claude-panel.js +77 -5
- 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-image-gallery.js +50 -47
- package/neural-interface/public/shared/ui-keybinds.js +26 -1
- package/neural-interface/public/shared/ui-menubar.js +8 -18
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
- package/neural-interface/public/shared/ui-navbar.js +4 -24
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
- package/neural-interface/public/shared/ui-sessions.js +5 -3
- package/neural-interface/public/shared/ui-settings.js +594 -462
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
- 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 +124 -180
- package/neural-interface/public/shared/ui-tool-updates.js +3 -1
- package/neural-interface/public/shared/ui-tooltip.js +10 -5
- 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 +311 -58
- package/neural-interface/public/shared/ui-worker-end.js +4 -0
- 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 +1 -1
- 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-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- 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 +3934 -2454
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +18 -12
- package/package.json +16 -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 +4 -4
- 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
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SETTINGS — the OpenCode SynaBun found, in words
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What `GET /api/opencode/status` (`installed`, server.js opencodeInstallState) and
|
|
6
|
+
// `POST /api/cli/detect/opencode` (`support`) say about the OpenCode binary: which version it is,
|
|
7
|
+
// or why SynaBun does not drive it and how to get OpenCode 2. The server decides
|
|
8
|
+
// (lib/opencode-protocol.js opencodeSupportFor gives `code`); the page words it in its own
|
|
9
|
+
// language. Pure: no DOM and no imports, so node runs it (tests/settings-opencode-view.test.mjs).
|
|
10
|
+
//
|
|
11
|
+
// describeOpencodeInstall(installed, { th, tx }) → { tone, status, html, text }
|
|
12
|
+
//
|
|
13
|
+
// `installed` is `{ installed, version, supported, code, path, minimum, commands }`: `supported`
|
|
14
|
+
// is true for a version SynaBun drives, false for one it refuses (`code` says why: 'opencode1',
|
|
15
|
+
// 'outdated', 'newer'), null while the version is not known (a command line, a version that could
|
|
16
|
+
// not be read: the serve's own answer decides when it starts). `th(key, html)` and `tx(key, params)`
|
|
17
|
+
// are the kit's: `th` escapes the string and puts raw HTML in its `{names}` (every value here is
|
|
18
|
+
// escaped first), `tx` gives the plain text, for a toast.
|
|
19
|
+
|
|
20
|
+
const esc = (value) => String(value ?? '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
21
|
+
const code = (text) => `<code class="stg-code">${esc(text)}</code>`;
|
|
22
|
+
|
|
23
|
+
// Written out whole, so the key checks find every one (tests/settings-ia.test.mjs, build_en.py --used).
|
|
24
|
+
const REFUSAL_KEYS = {
|
|
25
|
+
opencode1: 'settings.redesign.opencode.refusal.opencode1',
|
|
26
|
+
outdated: 'settings.redesign.opencode.refusal.outdated',
|
|
27
|
+
newer: 'settings.redesign.opencode.refusal.newer',
|
|
28
|
+
};
|
|
29
|
+
const UPDATE_COMMAND = 'opencode upgrade';
|
|
30
|
+
|
|
31
|
+
/** "2.1.4" from "opencode v2.1.4" (a prerelease kept whole), as the server's own sentence shows it. */
|
|
32
|
+
export function shownOpencodeVersion(version) {
|
|
33
|
+
return String(version ?? '').trim().replace(/^opencode\s+/i, '').replace(/^v(?=\d)/i, '');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function commandList(commands) {
|
|
37
|
+
return (Array.isArray(commands) ? commands : []).map((command) => String(command ?? '').trim()).filter(Boolean);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The install commands as one phrase: `a`, or `a or b` (each its own <code> in the HTML). */
|
|
41
|
+
export function opencodeCommandsPhrase(commands, { th, tx }) {
|
|
42
|
+
const list = commandList(commands);
|
|
43
|
+
const join = (words, wrap, translate) => words.slice(1).reduce(
|
|
44
|
+
(phrase, command) => translate('settings.redesign.opencode.commandOr', { first: phrase, second: wrap(command) }),
|
|
45
|
+
wrap(words[0]),
|
|
46
|
+
);
|
|
47
|
+
if (!list.length) return { html: '', text: '' };
|
|
48
|
+
return { html: join(list, code, th), text: join(list, (command) => command, tx) };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The note under the OpenCode service bar: `tone` '' | 'ok' | 'warn', `status` the short word for
|
|
53
|
+
* the status bar when OpenCode cannot run ('notInstalled' | 'notSupported' | null), `html` the note
|
|
54
|
+
* (empty: no note), `text` the same sentence as plain text.
|
|
55
|
+
*/
|
|
56
|
+
export function describeOpencodeInstall(installed, { th, tx }) {
|
|
57
|
+
const none = { tone: '', status: null, html: '', text: '' };
|
|
58
|
+
if (!installed || typeof installed !== 'object') return none;
|
|
59
|
+
const commands = opencodeCommandsPhrase(installed.commands, { th, tx });
|
|
60
|
+
if (installed.installed === false) {
|
|
61
|
+
return {
|
|
62
|
+
tone: 'warn',
|
|
63
|
+
status: 'notInstalled',
|
|
64
|
+
html: th('settings.redesign.opencode.notInstalled', { commands: commands.html }),
|
|
65
|
+
text: tx('settings.redesign.opencode.notInstalled', { commands: commands.text }),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const version = shownOpencodeVersion(installed.version);
|
|
69
|
+
const path = String(installed.path || 'opencode');
|
|
70
|
+
if (installed.supported === false && REFUSAL_KEYS[installed.code]) {
|
|
71
|
+
const key = REFUSAL_KEYS[installed.code];
|
|
72
|
+
const minimum = String(installed.minimum || '');
|
|
73
|
+
return {
|
|
74
|
+
tone: 'warn',
|
|
75
|
+
status: 'notSupported',
|
|
76
|
+
html: th(key, { path: code(path), version: esc(version), commands: commands.html, update: code(UPDATE_COMMAND), minimum: esc(minimum) }),
|
|
77
|
+
text: tx(key, { path, version, commands: commands.text, update: UPDATE_COMMAND, minimum }),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (installed.supported === true && version) {
|
|
81
|
+
return {
|
|
82
|
+
tone: 'ok',
|
|
83
|
+
status: null,
|
|
84
|
+
html: th('settings.redesign.opencode.installedVersion', { version: esc(version), path: code(path) }),
|
|
85
|
+
text: tx('settings.redesign.opencode.installedVersion', { version, path }),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return none;
|
|
89
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Settings → Skills: the skill folders block
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// Under the skill chips: the move of SynaBun's old Codex prompt / OpenCode command bridges to native skill
|
|
6
|
+
// folders (review it, move them, undo the last move) and the switch that lets installs go into the shared
|
|
7
|
+
// ~/.agents/skills. Nothing moves when Settings opens: the owner reads the plan and presses Move (the server checks
|
|
8
|
+
// the plan's digest, installs the native folder first, keeps each bridge's bytes in the move's journal, and leaves
|
|
9
|
+
// every file that is not SynaBun's where it is). Words and states come from settings-skill-folders.js; requests
|
|
10
|
+
// carry the X-SynaBun-UI header the owner-only routes ask for (api.js). A change made anywhere else (another window,
|
|
11
|
+
// the Studio, an agent's submission, an install) reaches the block and the chips through `skills:changed`, in place,
|
|
12
|
+
// never under a plan the owner is reading.
|
|
13
|
+
|
|
14
|
+
import * as kit from './settings-kit.js';
|
|
15
|
+
import { chipState, installedToast, lastUndoable, readFoldersStatus, readMigrationOutcome, readMigrationPlan, readRevertOutcome, toggledAssistants } from './settings-skill-folders.js';
|
|
16
|
+
import { fetchClaudeCodeSkills, fetchSkillsConfig, fetchSkillsMigrations, fetchSkillsStatus, planSkillsBridgeMigration, planSkillsBridgeMigrationConfirmed, revertSkillsMigration, runSkillsBridgeMigration, saveSkillsConfig } from '../api.js';
|
|
17
|
+
import { on } from '../state.js';
|
|
18
|
+
|
|
19
|
+
export { chipState, installedToast };
|
|
20
|
+
|
|
21
|
+
const params = (p) => Object.fromEntries(Object.entries(p || {}).map(([k, v]) => [k, v && typeof v === 'object' && typeof v.key === 'string' ? kit.tx(v.key) : v]));
|
|
22
|
+
const say = (m) => (m ? kit.tx(m.key, params(m.params)) : '');
|
|
23
|
+
|
|
24
|
+
/** The block's markup (filled in by wireSkillFolders once the server answers). */
|
|
25
|
+
export function skillFoldersHTML() {
|
|
26
|
+
return `
|
|
27
|
+
<div class="stg-skill-folders" id="stg-skill-folders">
|
|
28
|
+
${kit.subhead(kit.te('settings.redesign.skills.folders.title'))}
|
|
29
|
+
<div class="stg-help" id="stg-skill-folders-status" role="status" aria-live="polite" style="margin-top:0">${kit.te('settings.redesign.skills.folders.loading')}</div>
|
|
30
|
+
<div class="stg-help" id="stg-skill-folders-others" hidden></div>
|
|
31
|
+
<ul class="stg-help" id="stg-skill-folders-plan" hidden></ul>
|
|
32
|
+
<div class="cc-skill-targets" role="group" id="stg-skill-folders-actions">
|
|
33
|
+
<button type="button" class="cc-skill-target" id="stg-skill-folders-review" hidden>${kit.te('settings.redesign.skills.folders.review')}</button>
|
|
34
|
+
<button type="button" class="cc-skill-target" id="stg-skill-folders-move" hidden>${kit.te('settings.redesign.skills.folders.move')}</button>
|
|
35
|
+
<button type="button" class="cc-skill-target" id="stg-skill-folders-cancel" hidden>${kit.te('settings.redesign.skills.folders.cancel')}</button>
|
|
36
|
+
<button type="button" class="cc-skill-target" id="stg-skill-folders-undo" hidden>${kit.te('settings.redesign.skills.folders.undo')}</button>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="cc-skill-row" id="stg-skill-folders-assistants" hidden>
|
|
39
|
+
<div class="cc-skill-info">
|
|
40
|
+
<span class="cc-skill-name">${kit.te('settings.redesign.skills.assistants.title')}</span>
|
|
41
|
+
<span class="cc-skill-desc" id="stg-skill-folders-assistants-help">${kit.te('settings.redesign.skills.assistants.help')}</span>
|
|
42
|
+
</div>
|
|
43
|
+
<div class="cc-skill-targets" role="group" aria-label="${kit.te('settings.redesign.skills.assistants.title')}" id="stg-skill-folders-assistants-chips"></div>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="cc-skill-row" id="stg-skill-folders-shared" hidden>
|
|
46
|
+
<div class="cc-skill-info">
|
|
47
|
+
<span class="cc-skill-name">${kit.te('settings.redesign.skills.folders.sharedLabel')}</span>
|
|
48
|
+
<span class="cc-skill-desc">${kit.te('settings.redesign.skills.folders.sharedHelp')}</span>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="cc-skill-targets"><button type="button" class="cc-skill-target" id="stg-skill-folders-shared-toggle" aria-pressed="false">${kit.te('settings.redesign.skills.folders.sharedOff')}</button></div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Read the skills again and redraw each runtime chip in place (its state, pressed, title). A chip that is being
|
|
57
|
+
* clicked is left to its own request.
|
|
58
|
+
* @param {HTMLElement} root the Settings overlay
|
|
59
|
+
*/
|
|
60
|
+
export async function refreshSkillChips(root) {
|
|
61
|
+
let skills;
|
|
62
|
+
try { skills = (await fetchClaudeCodeSkills())?.skills; } catch { return; }
|
|
63
|
+
if (!Array.isArray(skills)) return;
|
|
64
|
+
const byDir = new Map(skills.map((skill) => [skill.dirName, skill]));
|
|
65
|
+
for (const chip of root.querySelectorAll('.cc-skill-target[data-cc-skill][data-cc-target]')) {
|
|
66
|
+
const skill = byDir.get(chip.dataset.ccSkill);
|
|
67
|
+
if (!skill || chip.style.pointerEvents === 'none') continue;
|
|
68
|
+
const target = chip.dataset.ccTarget;
|
|
69
|
+
const installed = skill.installedTargets || { claude: !!skill.installed, codex: false, opencode: false };
|
|
70
|
+
const now = chipState(skill.targetStates?.[target], !!installed[target]);
|
|
71
|
+
chip.classList.toggle('on', now.on);
|
|
72
|
+
chip.dataset.state = now.on ? 'on' : 'off';
|
|
73
|
+
chip.dataset.installState = now.state;
|
|
74
|
+
chip.setAttribute('aria-pressed', now.on ? 'true' : 'false');
|
|
75
|
+
chip.title = kit.tx(now.key, { skill: skill.name || skill.dirName, assistant: chip.textContent.trim() });
|
|
76
|
+
const row = chip.closest('.cc-skill-row');
|
|
77
|
+
if (row) row.classList.toggle('installed', !!row.querySelector('.cc-skill-target.on'));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Fill the block in and wire its buttons.
|
|
83
|
+
* @param {HTMLElement} root the Settings overlay
|
|
84
|
+
* @param {{ toast?: (msg: string, ms?: number) => void, onChanged?: () => void, signal?: AbortSignal }} [o]
|
|
85
|
+
* `onChanged`: something here (or elsewhere) changed the skills; `signal`: the panel's life (its listeners end with it)
|
|
86
|
+
*/
|
|
87
|
+
export async function wireSkillFolders(root, { toast = () => {}, onChanged = () => {}, signal = null } = {}) {
|
|
88
|
+
const box = root.querySelector('#stg-skill-folders');
|
|
89
|
+
if (!box) return;
|
|
90
|
+
const $ = (id) => box.querySelector(`#${id}`);
|
|
91
|
+
const status = $('stg-skill-folders-status');
|
|
92
|
+
const others = $('stg-skill-folders-others');
|
|
93
|
+
const planList = $('stg-skill-folders-plan');
|
|
94
|
+
const review = $('stg-skill-folders-review');
|
|
95
|
+
const move = $('stg-skill-folders-move');
|
|
96
|
+
const cancel = $('stg-skill-folders-cancel');
|
|
97
|
+
const undo = $('stg-skill-folders-undo');
|
|
98
|
+
const shared = $('stg-skill-folders-shared');
|
|
99
|
+
const sharedToggle = $('stg-skill-folders-shared-toggle');
|
|
100
|
+
const assistantsRow = $('stg-skill-folders-assistants');
|
|
101
|
+
const assistantChips = $('stg-skill-folders-assistants-chips');
|
|
102
|
+
const assistantsHelp = $('stg-skill-folders-assistants-help');
|
|
103
|
+
let plan = null;
|
|
104
|
+
let undoable = null;
|
|
105
|
+
let working = false;
|
|
106
|
+
let assistants = null;
|
|
107
|
+
const ticked = new Set(); // the offered bridges the owner ticked ("Move it too")
|
|
108
|
+
|
|
109
|
+
const busy = (yes) => { working = yes; for (const b of [review, move, cancel, undo, sharedToggle, ...assistantChips.querySelectorAll('button'), ...planList.querySelectorAll('input')]) if (b) b.disabled = yes; };
|
|
110
|
+
const showPlan = (reading) => {
|
|
111
|
+
planList.replaceChildren(...reading.lines.map((line) => {
|
|
112
|
+
const li = document.createElement('li');
|
|
113
|
+
li.dataset.tone = line.tone;
|
|
114
|
+
if (line.offer) {
|
|
115
|
+
// A bridge file of a shipped skill that is not proven SynaBun's: moved only when the owner ticks it here.
|
|
116
|
+
const label = document.createElement('label');
|
|
117
|
+
label.className = 'stg-skill-folders-offer';
|
|
118
|
+
const box = document.createElement('input');
|
|
119
|
+
box.type = 'checkbox'; box.checked = !!line.offer.on; box.dataset.bridge = line.bridgeId; box.dataset.digest = line.offer.digest;
|
|
120
|
+
box.setAttribute('aria-label', kit.tx('settings.redesign.skills.folders.offerTick', { name: line.params.name }));
|
|
121
|
+
const text = document.createElement('span');
|
|
122
|
+
text.textContent = say(line);
|
|
123
|
+
label.append(box, text);
|
|
124
|
+
li.append(label);
|
|
125
|
+
} else li.textContent = say(line);
|
|
126
|
+
return li;
|
|
127
|
+
}));
|
|
128
|
+
planList.hidden = !reading.lines.length;
|
|
129
|
+
};
|
|
130
|
+
// A plan on screen: its lines, Move only when something would move, Cancel to put the block back.
|
|
131
|
+
const present = (next) => {
|
|
132
|
+
plan = next;
|
|
133
|
+
const reading = readMigrationPlan(plan, ticked);
|
|
134
|
+
showPlan(reading);
|
|
135
|
+
if (reading.empty) status.textContent = say(reading.empty);
|
|
136
|
+
move.hidden = reading.movable === 0;
|
|
137
|
+
cancel.hidden = false;
|
|
138
|
+
review.hidden = true;
|
|
139
|
+
};
|
|
140
|
+
// An offer ticked or unticked: the plan is asked again with the confirmations (a confirmed plan is another plan,
|
|
141
|
+
// taken by its own digest), and shown.
|
|
142
|
+
planList.addEventListener('change', async (e) => {
|
|
143
|
+
const box = e.target;
|
|
144
|
+
if (!box?.dataset?.bridge) return;
|
|
145
|
+
if (box.checked) ticked.add(box.dataset.bridge); else ticked.delete(box.dataset.bridge);
|
|
146
|
+
busy(true);
|
|
147
|
+
try {
|
|
148
|
+
const confirm = Object.fromEntries([...planList.querySelectorAll('input[data-bridge]')].filter((b) => ticked.has(b.dataset.bridge)).map((b) => [b.dataset.bridge, b.dataset.digest]));
|
|
149
|
+
present((await planSkillsBridgeMigrationConfirmed(confirm)).plan);
|
|
150
|
+
} catch (err) { toast(kit.tx('settings.redesign.skills.folders.failed', { message: err.message }), 6000); }
|
|
151
|
+
finally { busy(false); }
|
|
152
|
+
});
|
|
153
|
+
const drawAssistants = () => {
|
|
154
|
+
if (!assistants) { assistantsRow.hidden = true; return; }
|
|
155
|
+
assistantsRow.hidden = false;
|
|
156
|
+
assistantChips.replaceChildren(...assistants.chips.map((chip) => {
|
|
157
|
+
const b = document.createElement('button');
|
|
158
|
+
b.type = 'button';
|
|
159
|
+
b.className = `cc-skill-target${chip.on ? ' on' : ''}`;
|
|
160
|
+
b.dataset.assistant = chip.id;
|
|
161
|
+
b.setAttribute('aria-pressed', chip.on ? 'true' : 'false');
|
|
162
|
+
b.textContent = kit.tx(chip.key);
|
|
163
|
+
b.title = kit.tx(chip.on ? 'settings.redesign.skills.assistants.chipOn' : 'settings.redesign.skills.assistants.chipOff', { assistant: kit.tx(chip.key) });
|
|
164
|
+
return b;
|
|
165
|
+
}));
|
|
166
|
+
const detected = assistants.detectedWords.map((w) => kit.tx(w.key)).join(', ');
|
|
167
|
+
assistantsHelp.textContent = `${kit.tx('settings.redesign.skills.assistants.help')} ${kit.tx(assistants.auto ? 'settings.redesign.skills.assistants.detected' : 'settings.redesign.skills.assistants.chosen', { detected })}`;
|
|
168
|
+
if (!assistants.auto) {
|
|
169
|
+
const reset = document.createElement('button');
|
|
170
|
+
reset.type = 'button'; reset.className = 'cc-skill-target'; reset.dataset.assistant = 'auto';
|
|
171
|
+
reset.textContent = kit.tx('settings.redesign.skills.assistants.useDetected');
|
|
172
|
+
reset.title = kit.tx('settings.redesign.skills.assistants.detected', { detected });
|
|
173
|
+
assistantChips.append(reset);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
assistantChips.addEventListener('click', async (e) => {
|
|
177
|
+
const chip = e.target.closest?.('button[data-assistant]');
|
|
178
|
+
if (!chip || chip.disabled || !assistants) return;
|
|
179
|
+
const next = chip.dataset.assistant === 'auto' ? null : toggledAssistants(assistants.effective, chip.dataset.assistant);
|
|
180
|
+
busy(true);
|
|
181
|
+
try { await saveSkillsConfig({ assistants: next }); }
|
|
182
|
+
catch (err) { toast(kit.tx('settings.redesign.skills.folders.failed', { message: err.message }), 6000); }
|
|
183
|
+
finally { busy(false); }
|
|
184
|
+
await refresh();
|
|
185
|
+
});
|
|
186
|
+
async function refresh() {
|
|
187
|
+
let answer = null;
|
|
188
|
+
try { answer = await fetchSkillsStatus(); } catch { answer = null; }
|
|
189
|
+
const reading = readFoldersStatus(answer);
|
|
190
|
+
status.textContent = say(reading.message);
|
|
191
|
+
if (!reading.available) { for (const el of [others, planList, review, move, cancel, undo, shared, assistantsRow]) el.hidden = true; return; }
|
|
192
|
+
// The owner's assistants: the status carries them on a server with the preference; GET /config is the fuller read.
|
|
193
|
+
assistants = reading.assistants;
|
|
194
|
+
if (assistants) { try { const cfg = await fetchSkillsConfig(); if (cfg?.assistants) assistants = readFoldersStatus({ ok: true, bridges: {}, assistants: cfg.assistants }).assistants; } catch { /* the status' view */ } }
|
|
195
|
+
drawAssistants();
|
|
196
|
+
ticked.clear();
|
|
197
|
+
others.hidden = !reading.others;
|
|
198
|
+
others.textContent = say(reading.others);
|
|
199
|
+
review.hidden = !reading.canReview;
|
|
200
|
+
move.hidden = true; cancel.hidden = true; planList.hidden = true; plan = null;
|
|
201
|
+
shared.hidden = false;
|
|
202
|
+
sharedToggle.classList.toggle('on', reading.sharedAgentsRoot);
|
|
203
|
+
sharedToggle.setAttribute('aria-pressed', reading.sharedAgentsRoot ? 'true' : 'false');
|
|
204
|
+
sharedToggle.textContent = kit.tx(reading.sharedAgentsRoot ? 'settings.redesign.skills.folders.sharedOn' : 'settings.redesign.skills.folders.sharedOff');
|
|
205
|
+
try { undoable = lastUndoable((await fetchSkillsMigrations()).migrations); } catch { undoable = null; }
|
|
206
|
+
undo.hidden = !undoable;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
review.addEventListener('click', async () => {
|
|
210
|
+
busy(true);
|
|
211
|
+
try {
|
|
212
|
+
present((await planSkillsBridgeMigration()).plan);
|
|
213
|
+
} catch (err) { toast(kit.tx('settings.redesign.skills.folders.failed', { message: err.message }), 6000); }
|
|
214
|
+
finally { busy(false); }
|
|
215
|
+
});
|
|
216
|
+
cancel.addEventListener('click', () => refresh());
|
|
217
|
+
move.addEventListener('click', async () => {
|
|
218
|
+
const reading = readMigrationPlan(plan, ticked);
|
|
219
|
+
if (!reading.planDigest) return;
|
|
220
|
+
busy(true);
|
|
221
|
+
try {
|
|
222
|
+
const out = await runSkillsBridgeMigration(reading.planDigest, reading.confirm);
|
|
223
|
+
toast(say(readMigrationOutcome(out)), 8000);
|
|
224
|
+
onChanged();
|
|
225
|
+
} catch (err) {
|
|
226
|
+
// The bridges or their destinations changed since the plan was shown: nothing moved, the new plan is shown.
|
|
227
|
+
if (err.code === 'plan_changed' && err.body?.plan) { present(err.body.plan); toast(kit.tx('settings.redesign.skills.folders.planChanged'), 8000); return; }
|
|
228
|
+
toast(kit.tx('settings.redesign.skills.folders.failed', { message: err.message }), 6000);
|
|
229
|
+
} finally { busy(false); }
|
|
230
|
+
await refresh();
|
|
231
|
+
});
|
|
232
|
+
undo.addEventListener('click', async () => {
|
|
233
|
+
if (!undoable) return;
|
|
234
|
+
busy(true);
|
|
235
|
+
try {
|
|
236
|
+
const out = await revertSkillsMigration(undoable.journalId);
|
|
237
|
+
toast(say(readRevertOutcome(out)), 8000);
|
|
238
|
+
onChanged();
|
|
239
|
+
} catch (err) { toast(kit.tx('settings.redesign.skills.folders.failed', { message: err.message }), 6000); }
|
|
240
|
+
finally { busy(false); }
|
|
241
|
+
await refresh();
|
|
242
|
+
});
|
|
243
|
+
sharedToggle.addEventListener('click', async () => {
|
|
244
|
+
const next = sharedToggle.getAttribute('aria-pressed') !== 'true';
|
|
245
|
+
busy(true);
|
|
246
|
+
try { await saveSkillsConfig({ sharedAgentsRoot: next }); }
|
|
247
|
+
catch (err) { toast(kit.tx('settings.redesign.skills.folders.failed', { message: err.message }), 6000); }
|
|
248
|
+
finally { busy(false); }
|
|
249
|
+
await refresh();
|
|
250
|
+
});
|
|
251
|
+
// Changes from anywhere else: the chips follow at once; the block follows unless a plan is on screen or a request
|
|
252
|
+
// of its own is under way (a move is taken only by the digest of the plan shown, so a stale plan moves nothing).
|
|
253
|
+
let soon = 0;
|
|
254
|
+
const stop = on('sync:skills:changed', () => {
|
|
255
|
+
clearTimeout(soon);
|
|
256
|
+
soon = setTimeout(() => {
|
|
257
|
+
if (!box.isConnected) { stop(); return; }
|
|
258
|
+
onChanged();
|
|
259
|
+
if (!plan && !working) refresh();
|
|
260
|
+
}, 300);
|
|
261
|
+
});
|
|
262
|
+
signal?.addEventListener('abort', () => { stop(); clearTimeout(soon); }, { once: true });
|
|
263
|
+
if (signal?.aborted) { stop(); return; }
|
|
264
|
+
await refresh();
|
|
265
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Settings → Skills: skill folders, read (no DOM)
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What the Skills Service says (GET /api/skills-studio/v2/status, the bridge migration's plan and outcome) turned
|
|
6
|
+
// into what Settings → Skills shows: whether the old Codex prompt / OpenCode command bridges can move to native
|
|
7
|
+
// skill folders, what each would do, the outcome of a move or an undo, the shared ~/.agents/skills switch, and
|
|
8
|
+
// each runtime chip's state (a native folder, an old bridge, someone else's folder). Every sentence is a
|
|
9
|
+
// settings.redesign.skills.* key; the pane (settings-skill-folders-pane.js) draws it.
|
|
10
|
+
|
|
11
|
+
const RUNTIME = { claude: 'settings.redesign.product.claude', codex: 'settings.redesign.product.codex', opencode: 'settings.redesign.product.opencode' };
|
|
12
|
+
|
|
13
|
+
/** What the status answer says, or that the server cannot say it yet (one from before the Skills Service). */
|
|
14
|
+
export function readFoldersStatus(answer) {
|
|
15
|
+
if (!answer || answer.ok !== true || !answer.bridges) return { available: false, message: { key: 'settings.redesign.skills.folders.unavailable' } };
|
|
16
|
+
const synabun = Number(answer.bridges.synabun) || 0;
|
|
17
|
+
const other = Number(answer.bridges.other) || 0;
|
|
18
|
+
return {
|
|
19
|
+
available: true,
|
|
20
|
+
sharedAgentsRoot: answer.config?.sharedAgentsRoot === true,
|
|
21
|
+
assistants: readAssistants(answer.assistants),
|
|
22
|
+
bridges: { synabun, other },
|
|
23
|
+
message: synabun === 0 ? { key: 'settings.redesign.skills.folders.bridgesNone' } : synabun === 1 ? { key: 'settings.redesign.skills.folders.bridgesOne' } : { key: 'settings.redesign.skills.folders.bridgesMany', params: { count: synabun } },
|
|
24
|
+
others: other === 0 ? null : other === 1 ? { key: 'settings.redesign.skills.folders.othersOne' } : { key: 'settings.redesign.skills.folders.othersMany', params: { count: other } },
|
|
25
|
+
canReview: synabun > 0,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const ASSISTANTS = ['claude', 'codex', 'opencode'];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The owner's assistants (GET /v2/config `assistants`: chosen, detected, effective): one chip per runtime, on when
|
|
33
|
+
* it is in effect, and whether the list is the detected one (nothing chosen yet). A server from before this has none.
|
|
34
|
+
*/
|
|
35
|
+
export function readAssistants(view) {
|
|
36
|
+
if (!view || !Array.isArray(view.effective)) return null;
|
|
37
|
+
const effective = view.effective.filter((r) => ASSISTANTS.includes(r));
|
|
38
|
+
const detected = (Array.isArray(view.detected) ? view.detected : []).filter((r) => ASSISTANTS.includes(r));
|
|
39
|
+
return {
|
|
40
|
+
auto: !Array.isArray(view.chosen),
|
|
41
|
+
effective, detected,
|
|
42
|
+
chips: ASSISTANTS.map((id) => ({ id, key: RUNTIME[id], on: effective.includes(id), detected: detected.includes(id) })),
|
|
43
|
+
detectedWords: detected.map((id) => ({ key: RUNTIME[id] })),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The list a chip's click sends (PUT /v2/config `assistants`): toggled, never empty (the last one stays on). */
|
|
48
|
+
export function toggledAssistants(effective, id) {
|
|
49
|
+
const now = (Array.isArray(effective) ? effective : []).filter((r) => ASSISTANTS.includes(r));
|
|
50
|
+
if (now.includes(id)) { const next = now.filter((r) => r !== id); return next.length ? next : now; }
|
|
51
|
+
return ASSISTANTS.filter((r) => now.includes(r) || r === id);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The lines of a migration plan, one per bridge of SynaBun's, and whether anything would move. The prompt and
|
|
56
|
+
* command files that are not SynaBun's are only counted (the status says how many): they are never moved, and a
|
|
57
|
+
* line each would bury the ones that are; except the bridge file of a skill SynaBun ships that is not proven its
|
|
58
|
+
* own (`offer`): that one is listed with a tick, "Move it too", and moves only once the owner confirms its bytes.
|
|
59
|
+
* @param {object} plan @param {Set<string>|string[]} [ticked] the offers the owner ticked (bridge ids)
|
|
60
|
+
*/
|
|
61
|
+
export function readMigrationPlan(plan, ticked = []) {
|
|
62
|
+
const steps = Array.isArray(plan?.steps) ? plan.steps : [];
|
|
63
|
+
const on = new Set(ticked);
|
|
64
|
+
const lines = steps.filter((s) => s.action !== 'leave' || s.proof !== 'none' || s.ledgerRecord || s.offer).map((s) => {
|
|
65
|
+
const params = { runtime: { key: RUNTIME[s.runtime] || RUNTIME.codex }, name: `/${s.name}`, destination: s.native?.destination || '', why: s.why || '' };
|
|
66
|
+
if (s.action === 'migrate') return { tone: 'ok', key: s.proof === 'owner-confirmed' ? 'settings.redesign.skills.folders.stepConfirmed' : 'settings.redesign.skills.folders.stepMigrate', params, bridgeId: s.bridgeId, offer: s.proof === 'owner-confirmed' ? { digest: s.digest, on: true } : null };
|
|
67
|
+
if (s.action === 'blocked') return { tone: 'warn', key: 'settings.redesign.skills.folders.stepBlocked', params };
|
|
68
|
+
if (s.offer) return { tone: 'offer', key: 'settings.redesign.skills.folders.stepOffer', params, bridgeId: s.bridgeId, offer: { digest: s.offer.digest, on: on.has(s.bridgeId) } };
|
|
69
|
+
return { tone: 'muted', key: 'settings.redesign.skills.folders.stepLeave', params };
|
|
70
|
+
});
|
|
71
|
+
const movable = steps.filter((s) => s.action === 'migrate').length;
|
|
72
|
+
const confirm = Object.fromEntries(steps.filter((s) => s.proof === 'owner-confirmed').map((s) => [s.bridgeId, s.digest]));
|
|
73
|
+
return { lines, movable, offered: steps.filter((s) => s.action === 'leave' && s.offer).length, confirm, planDigest: typeof plan?.planDigest === 'string' ? plan.planDigest : null, empty: movable === 0 ? { key: 'settings.redesign.skills.folders.nothing' } : null };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** What a move answered: done, in part (which and why), nothing to do. */
|
|
77
|
+
export function readMigrationOutcome(out) {
|
|
78
|
+
if (!out) return { tone: 'warn', key: 'settings.redesign.skills.folders.failed', params: { message: '' } };
|
|
79
|
+
if (out.nothingToDo) return { tone: 'muted', key: 'settings.redesign.skills.folders.nothing' };
|
|
80
|
+
if (out.status === 'done') return { tone: 'ok', key: 'settings.redesign.skills.folders.moved', params: { id: out.journalId } };
|
|
81
|
+
const failed = (out.results || []).filter((r) => r.outcome !== 'migrated').map((r) => `/${r.name}: ${r.error || r.outcome}`).join('; ');
|
|
82
|
+
if (out.status === 'failed') return { tone: 'warn', key: 'settings.redesign.skills.folders.failed', params: { message: failed } };
|
|
83
|
+
return { tone: 'warn', key: 'settings.redesign.skills.folders.movedPartly', params: { details: failed } };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** What an undo answered. */
|
|
87
|
+
export function readRevertOutcome(out) {
|
|
88
|
+
if (!out) return { tone: 'warn', key: 'settings.redesign.skills.folders.failed', params: { message: '' } };
|
|
89
|
+
if (out.status === 'reverted') return { tone: 'ok', key: 'settings.redesign.skills.folders.undone' };
|
|
90
|
+
const details = (out.results || []).filter((r) => r.bridge === 'conflict' || r.bridge === 'failed' || r.native === 'kept').map((r) => r.why || r.path).join('; ');
|
|
91
|
+
return { tone: 'warn', key: 'settings.redesign.skills.folders.undonePartly', params: { details } };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The last move that can be undone, from GET /migrations (newest first). */
|
|
95
|
+
export function lastUndoable(list) {
|
|
96
|
+
const rows = (Array.isArray(list) ? list : []).filter((m) => m && (m.status === 'done' || m.status === 'partial' || m.status === 'partially-reverted') && m.migrated > 0);
|
|
97
|
+
rows.sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1));
|
|
98
|
+
return rows[0] || null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A runtime chip of a skill: its state (`native`: SynaBun's skill folder; `bridge`: an old bridge not moved yet;
|
|
103
|
+
* `shared`: SynaBun's copy in ~/.agents/skills, which Codex and OpenCode both read; `other`: a folder of that name
|
|
104
|
+
* SynaBun did not install, in the runtime's folder or the shared one; `installed`: a server from before the skill
|
|
105
|
+
* folders says only that it is there; `off`) and its title key.
|
|
106
|
+
* @param {object|undefined} state the skill's targetStates[runtime] from GET /api/skills
|
|
107
|
+
* @param {boolean} installed the older single flag (installedTargets[runtime])
|
|
108
|
+
*/
|
|
109
|
+
export function chipState(state, installed) {
|
|
110
|
+
if (!state) return installed ? { state: 'installed', on: true, key: 'settings.redesign.skills.chip.installed' } : { state: 'off', on: false, key: 'settings.redesign.skills.installFor' };
|
|
111
|
+
if (state.native === 'native' || state.native === 'modified') return { state: 'native', on: true, key: 'settings.redesign.skills.chip.native' };
|
|
112
|
+
if (state.bridge) return { state: 'bridge', on: true, key: 'settings.redesign.skills.chip.bridge' };
|
|
113
|
+
if (state.shared === 'native') return { state: 'shared', on: true, key: 'settings.redesign.skills.chip.shared' };
|
|
114
|
+
if (state.native === 'other') return { state: 'other', on: false, key: 'settings.redesign.skills.chip.other' };
|
|
115
|
+
if (state.shared === 'other') return { state: 'other', on: false, key: 'settings.redesign.skills.chip.otherShared' };
|
|
116
|
+
return { state: 'off', on: false, key: 'settings.redesign.skills.installFor' };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The toast after a chip installed a skill: a skill folder of its own, whether an old bridge of it is still there,
|
|
121
|
+
* and what the runtime needs to see it (`refresh` of the install answer: automatic, reload-skills, new-session).
|
|
122
|
+
*/
|
|
123
|
+
export function installedToast(answer) {
|
|
124
|
+
if (answer?.shared) return 'settings.redesign.skills.chip.installedShared';
|
|
125
|
+
if (answer?.bridge) return 'settings.redesign.skills.chip.installedBridgeLeft';
|
|
126
|
+
const kinds = new Set((Array.isArray(answer?.refresh) ? answer.refresh : []).map((r) => r?.kind));
|
|
127
|
+
if (kinds.has('new-session')) return 'settings.redesign.skills.chip.installedNewSession';
|
|
128
|
+
if (kinds.has('reload-skills')) return 'settings.redesign.skills.chip.installedReload';
|
|
129
|
+
return 'settings.redesign.skills.chip.installedNative';
|
|
130
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SETTINGS — Protect workers, in words
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What `GET /api/assistant/worker-guard` (lib/assistant-api.js; the setting in lib/worker-guard-setting.js, what
|
|
6
|
+
// it means in lib/worker-confinement.js) says about Settings → Automations → Automation behavior → Protect
|
|
7
|
+
// workers: the mode in effect (enforce, warn, off), whether it can be changed, and what stands in the way. Pure:
|
|
8
|
+
// no DOM and no import, so node runs it (tests/settings-worker-guard-view.test.mjs).
|
|
9
|
+
//
|
|
10
|
+
// readWorkerGuardResponse({ status, body, error }) what the server answered → a reading
|
|
11
|
+
// describeWorkerGuard(reading, { tx }) the row as ui-settings.js draws it
|
|
12
|
+
// describeWorkerGuardSaveFailure(input, { tx }) the sentence for a choice that could not be saved
|
|
13
|
+
//
|
|
14
|
+
// `tx(key, params)` is the caller's translate function over the settings.redesign.* strings (the kit's `tx`).
|
|
15
|
+
// The row never shows a healthy-looking control for something that is not happening: a server started before
|
|
16
|
+
// the setting existed shows the default, disabled, and says a restart is missing.
|
|
17
|
+
|
|
18
|
+
const K = 'settings.redesign.hooks.workerGuard';
|
|
19
|
+
|
|
20
|
+
export const WORKER_GUARD_MODES = Object.freeze(['enforce', 'warn', 'off']);
|
|
21
|
+
export const WORKER_GUARD_DEFAULT = 'enforce';
|
|
22
|
+
|
|
23
|
+
const clean = (value, max = 200) => {
|
|
24
|
+
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
25
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* What the server answered.
|
|
30
|
+
*
|
|
31
|
+
* ready the setting is there: `mode` is in effect, `modes` the choices
|
|
32
|
+
* old-server 404: the running server was started before the setting existed
|
|
33
|
+
* failed 503 WORKER_GUARD_UNAVAILABLE: the setting did not start; `reason` is the error
|
|
34
|
+
* unreadable anything else (no answer, a refusal, a body that is not the payload)
|
|
35
|
+
*/
|
|
36
|
+
export function readWorkerGuardResponse({ status = 0, body = null, error = null } = {}) {
|
|
37
|
+
const code = Number(status) || 0;
|
|
38
|
+
if (code === 404) return { state: 'old-server', mode: WORKER_GUARD_DEFAULT, modes: [...WORKER_GUARD_MODES], reason: null };
|
|
39
|
+
if (code === 503) return { state: 'failed', mode: WORKER_GUARD_DEFAULT, modes: [...WORKER_GUARD_MODES], reason: clean(body?.reason || body?.error) || null };
|
|
40
|
+
if (code >= 200 && code < 300 && body && typeof body === 'object' && body.ok !== false && WORKER_GUARD_MODES.includes(body.mode)) {
|
|
41
|
+
const modes = Array.isArray(body.modes) && body.modes.every((m) => WORKER_GUARD_MODES.includes(m)) && body.modes.length ? body.modes : [...WORKER_GUARD_MODES];
|
|
42
|
+
return { state: 'ready', mode: body.mode, modes, reason: null };
|
|
43
|
+
}
|
|
44
|
+
return { state: 'unreadable', mode: WORKER_GUARD_DEFAULT, modes: [...WORKER_GUARD_MODES], reason: clean(error || body?.error || (code ? `HTTP ${code}` : '')) || null };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The row: { ready, disabled, mode, options: [{ value, label, help }], note: { text, tone } | null, applies }. */
|
|
48
|
+
export function describeWorkerGuard(reading, { tx }) {
|
|
49
|
+
const r = reading || readWorkerGuardResponse({ status: 0 });
|
|
50
|
+
const options = (r.modes || WORKER_GUARD_MODES).map((value) => ({ value, label: tx(`${K}.mode.${value}`), help: tx(`${K}.modeHelp.${value}`) }));
|
|
51
|
+
let note = null;
|
|
52
|
+
if (r.state === 'old-server') note = { text: tx(`${K}.restart`), tone: 'warn' };
|
|
53
|
+
else if (r.state === 'failed') note = { text: r.reason ? tx(`${K}.failed`, { reason: r.reason }) : tx(`${K}.failedNoReason`), tone: 'warn' };
|
|
54
|
+
else if (r.state === 'unreadable') note = { text: r.reason ? tx(`${K}.unreadable`, { reason: r.reason }) : tx(`${K}.unreadableNoReason`), tone: 'warn' };
|
|
55
|
+
return {
|
|
56
|
+
ready: r.state === 'ready',
|
|
57
|
+
disabled: r.state !== 'ready',
|
|
58
|
+
mode: r.mode || WORKER_GUARD_DEFAULT,
|
|
59
|
+
options,
|
|
60
|
+
note,
|
|
61
|
+
applies: tx(`${K}.applies`),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The sentence under the row when a choice could not be saved: { status, body, error, mode }. */
|
|
66
|
+
export function describeWorkerGuardSaveFailure({ status = 0, body = null, error = null, mode = '' } = {}, { tx }) {
|
|
67
|
+
const code = Number(status) || 0;
|
|
68
|
+
const name = tx(`${K}.mode.${WORKER_GUARD_MODES.includes(mode) ? mode : WORKER_GUARD_DEFAULT}`);
|
|
69
|
+
if (code === 404) return tx(`${K}.save.oldServer`, { mode: name });
|
|
70
|
+
if (!code) return tx(`${K}.save.noAnswer`, { mode: name, reason: clean(error) || '' });
|
|
71
|
+
return tx(`${K}.save.failed`, { mode: name, reason: clean(body?.error || error) || `HTTP ${code}` });
|
|
72
|
+
}
|