synabun 2.0.7 → 2.0.10
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 +67 -0
- package/README.md +1 -1
- package/THIRD-PARTY-LICENSES.md +46 -4
- package/hooks/claude-code/post-plan.mjs +6 -30
- package/hooks/claude-code/session-start.mjs +1 -4
- package/hooks/claude-code/shared.mjs +106 -72
- package/launcher.mjs +9 -3
- package/lib/package-processes.js +670 -0
- package/lib/runtime-dependencies.js +225 -0
- package/lib/start-launcher.js +6 -1
- package/mcp-server/dist/services/browser-bench.d.ts +1 -1
- package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/browser-bench.js +1 -1
- package/mcp-server/dist/services/browser-bench.js.map +1 -1
- package/mcp-server/dist/services/categories.d.ts +35 -2
- package/mcp-server/dist/services/categories.d.ts.map +1 -1
- package/mcp-server/dist/services/categories.js +77 -33
- package/mcp-server/dist/services/categories.js.map +1 -1
- package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
- package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/desktop-bench.js +1 -1
- package/mcp-server/dist/services/desktop-bench.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +1 -1
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +1 -1
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/bluesky.js +1 -1
- package/mcp-server/dist/tools/bluesky.js.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.js +1 -1
- package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-batch.js +1 -1
- package/mcp-server/dist/tools/browser-batch.js.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-interact.js +1 -1
- package/mcp-server/dist/tools/browser-interact.js.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.js +1 -1
- package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-observe.js +1 -1
- package/mcp-server/dist/tools/browser-observe.js.map +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/card-tools.js +1 -1
- package/mcp-server/dist/tools/card-tools.js.map +1 -1
- package/mcp-server/dist/tools/category.d.ts +1 -1
- package/mcp-server/dist/tools/category.d.ts.map +1 -1
- package/mcp-server/dist/tools/category.js +5 -1
- package/mcp-server/dist/tools/category.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +1 -1
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +1 -1
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/computer.d.ts +1 -1
- package/mcp-server/dist/tools/computer.d.ts.map +1 -1
- package/mcp-server/dist/tools/computer.js +1 -1
- package/mcp-server/dist/tools/computer.js.map +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-channel.js +1 -1
- package/mcp-server/dist/tools/discord-channel.js.map +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-guild.js +1 -1
- package/mcp-server/dist/tools/discord-guild.js.map +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-member.js +1 -1
- package/mcp-server/dist/tools/discord-member.js.map +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-message.js +1 -1
- package/mcp-server/dist/tools/discord-message.js.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-role.js +1 -1
- package/mcp-server/dist/tools/discord-role.js.map +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-thread.js +1 -1
- package/mcp-server/dist/tools/discord-thread.js.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.js +1 -1
- package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
- package/mcp-server/dist/tools/fb-groups.js +1 -1
- package/mcp-server/dist/tools/fb-groups.js.map +1 -1
- package/mcp-server/dist/tools/forget.d.ts +1 -1
- package/mcp-server/dist/tools/forget.d.ts.map +1 -1
- package/mcp-server/dist/tools/forget.js +1 -1
- package/mcp-server/dist/tools/forget.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +1 -1
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.js +1 -1
- package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.js +1 -1
- package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.js +1 -1
- package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/image-tools.js.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
- package/mcp-server/dist/tools/loop.d.ts +1 -1
- package/mcp-server/dist/tools/loop.d.ts.map +1 -1
- package/mcp-server/dist/tools/loop.js +1 -1
- package/mcp-server/dist/tools/loop.js.map +1 -1
- package/mcp-server/dist/tools/memories.d.ts +1 -1
- package/mcp-server/dist/tools/memories.d.ts.map +1 -1
- package/mcp-server/dist/tools/memories.js +1 -1
- package/mcp-server/dist/tools/memories.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +1 -1
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/profile.d.ts +1 -1
- package/mcp-server/dist/tools/profile.d.ts.map +1 -1
- package/mcp-server/dist/tools/profile.js +1 -1
- package/mcp-server/dist/tools/profile.js.map +1 -1
- package/mcp-server/dist/tools/recall.d.ts +1 -1
- package/mcp-server/dist/tools/recall.d.ts.map +1 -1
- package/mcp-server/dist/tools/recall.js +1 -1
- package/mcp-server/dist/tools/recall.js.map +1 -1
- package/mcp-server/dist/tools/reflect.d.ts +1 -1
- package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
- package/mcp-server/dist/tools/reflect.js +1 -1
- package/mcp-server/dist/tools/reflect.js.map +1 -1
- package/mcp-server/dist/tools/remember.d.ts +1 -1
- package/mcp-server/dist/tools/remember.d.ts.map +1 -1
- package/mcp-server/dist/tools/remember.js +13 -11
- package/mcp-server/dist/tools/remember.js.map +1 -1
- package/mcp-server/dist/tools/restore.d.ts +1 -1
- package/mcp-server/dist/tools/restore.d.ts.map +1 -1
- package/mcp-server/dist/tools/restore.js +1 -1
- package/mcp-server/dist/tools/restore.js.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
- package/mcp-server/dist/tools/sync.d.ts +1 -1
- package/mcp-server/dist/tools/sync.d.ts.map +1 -1
- package/mcp-server/dist/tools/sync.js +1 -1
- package/mcp-server/dist/tools/sync.js.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
- package/mcp-server/dist/tools/utils.d.ts +1 -1
- package/mcp-server/dist/tools/utils.d.ts.map +1 -1
- package/mcp-server/dist/tools/utils.js +1 -1
- package/mcp-server/dist/tools/utils.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.js +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/download.js +1 -1
- package/mcp-server/dist/tools/youtube/download.js.map +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/media.js +1 -1
- package/mcp-server/dist/tools/youtube/media.js.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/source.js +1 -1
- package/mcp-server/dist/tools/youtube/source.js.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.js +4 -9
- package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
- package/mcp-server/dist/tui.js +16 -4
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/package-lock.json +3 -3
- package/mcp-server/package.json +3 -3
- package/mcp-server/src/services/browser-bench.ts +1 -1
- package/mcp-server/src/services/categories.ts +77 -32
- package/mcp-server/src/services/desktop-bench.ts +1 -1
- package/mcp-server/src/tools/agents.ts +1 -1
- package/mcp-server/src/tools/bluesky.ts +1 -1
- package/mcp-server/src/tools/browser-advanced.ts +1 -1
- package/mcp-server/src/tools/browser-batch.ts +1 -1
- package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
- package/mcp-server/src/tools/browser-interact.ts +1 -1
- package/mcp-server/src/tools/browser-navigate.ts +1 -1
- package/mcp-server/src/tools/browser-observe.ts +1 -1
- package/mcp-server/src/tools/card-tools.ts +1 -1
- package/mcp-server/src/tools/category.ts +6 -1
- package/mcp-server/src/tools/choice.ts +1 -1
- package/mcp-server/src/tools/computer.ts +1 -1
- package/mcp-server/src/tools/discord-channel.ts +1 -1
- package/mcp-server/src/tools/discord-guild.ts +1 -1
- package/mcp-server/src/tools/discord-member.ts +1 -1
- package/mcp-server/src/tools/discord-message.ts +1 -1
- package/mcp-server/src/tools/discord-onboarding.ts +1 -1
- package/mcp-server/src/tools/discord-role.ts +1 -1
- package/mcp-server/src/tools/discord-thread.ts +1 -1
- package/mcp-server/src/tools/discord-webhook.ts +1 -1
- package/mcp-server/src/tools/fb-groups.ts +1 -1
- package/mcp-server/src/tools/forget.ts +1 -1
- package/mcp-server/src/tools/git-tools.ts +1 -1
- package/mcp-server/src/tools/gsc/experience.ts +1 -1
- package/mcp-server/src/tools/gsc/indexing.ts +1 -1
- package/mcp-server/src/tools/gsc/inspect.ts +1 -1
- package/mcp-server/src/tools/gsc/navigate.ts +1 -1
- package/mcp-server/src/tools/gsc/performance.ts +1 -1
- package/mcp-server/src/tools/gsc/settings.ts +1 -1
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
- package/mcp-server/src/tools/loop.ts +1 -1
- package/mcp-server/src/tools/memories.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +1 -1
- package/mcp-server/src/tools/profile.ts +1 -1
- package/mcp-server/src/tools/recall.ts +1 -1
- package/mcp-server/src/tools/reflect.ts +1 -1
- package/mcp-server/src/tools/remember.ts +13 -12
- package/mcp-server/src/tools/restore.ts +1 -1
- package/mcp-server/src/tools/style-guide-tools.ts +1 -1
- package/mcp-server/src/tools/sync.ts +1 -1
- package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
- package/mcp-server/src/tools/utils.ts +1 -1
- package/mcp-server/src/tools/whatsapp-send-file.ts +1 -1
- package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
- package/mcp-server/src/tools/youtube/analytics.ts +1 -1
- package/mcp-server/src/tools/youtube/download.ts +1 -1
- package/mcp-server/src/tools/youtube/media.ts +1 -1
- package/mcp-server/src/tools/youtube/metadata.ts +1 -1
- package/mcp-server/src/tools/youtube/navigate.ts +1 -1
- package/mcp-server/src/tools/youtube/playlist.ts +1 -1
- package/mcp-server/src/tools/youtube/schedule.ts +1 -1
- package/mcp-server/src/tools/youtube/source.ts +1 -1
- package/mcp-server/src/tools/youtube/upload.ts +4 -12
- package/mcp-server/src/tui.ts +22 -9
- package/neural-interface/i18n/en.json +236 -7
- package/neural-interface/i18n/pt-BR.json +238 -9
- package/neural-interface/lib/account-usage.js +5 -3
- package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
- package/neural-interface/lib/agent-sandbox-setting.js +57 -0
- package/neural-interface/lib/app-controls-api.js +161 -0
- package/neural-interface/lib/assistant-api.js +40 -7
- package/neural-interface/lib/assistant-brains/opencode.js +13 -1
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +44 -0
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +57 -0
- package/neural-interface/lib/assistant-catalog.js +17 -2
- package/neural-interface/lib/assistant-dispatch.js +3 -0
- package/neural-interface/lib/assistant-envelope.js +19 -1
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
- package/neural-interface/lib/assistant-runtime.js +131 -8
- package/neural-interface/lib/claude-model-catalog.js +64 -14
- package/neural-interface/lib/codex-account-auth.js +4 -2
- package/neural-interface/lib/codex-accounts.js +14 -1
- package/neural-interface/lib/codex-settings-schema.js +111 -47
- package/neural-interface/lib/editor-completion-opencode.js +132 -35
- package/neural-interface/lib/exec-loop-command.js +11 -2
- package/neural-interface/lib/install-details.js +23 -5
- package/neural-interface/lib/launch-cache.js +293 -0
- package/neural-interface/lib/login-start.js +524 -0
- package/neural-interface/lib/native-loop-providers.js +24 -4
- package/neural-interface/lib/native-loop-runtime.js +19 -0
- package/neural-interface/lib/opencode-db-schema.js +75 -0
- package/neural-interface/lib/opencode-history.js +16 -1
- package/neural-interface/lib/opencode-protocol.js +173 -0
- package/neural-interface/lib/opencode-provider-view.js +81 -0
- package/neural-interface/lib/opencode-runtime-config.js +10 -3
- package/neural-interface/lib/opencode-serve-records.js +606 -0
- package/neural-interface/lib/opencode-v2-client.js +33 -6
- package/neural-interface/lib/opencode-v2-ws-requests.js +19 -5
- package/neural-interface/lib/opencode-worker-activity.js +229 -0
- package/neural-interface/lib/opencode2-client.js +760 -0
- package/neural-interface/lib/opencode2-proxy.js +116 -0
- package/neural-interface/lib/opencode2-translate.js +1008 -0
- package/neural-interface/lib/process-identity.js +361 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +3 -2
- package/neural-interface/lib/session-cache-builder.js +26 -5
- package/neural-interface/lib/session-indexer.js +3 -1
- package/neural-interface/lib/session-title-generator.js +20 -0
- package/neural-interface/lib/style-guide/api.js +4 -3
- package/neural-interface/lib/untrusted-file-headers.js +27 -0
- package/neural-interface/package-lock.json +1342 -44
- package/neural-interface/package.json +5 -1
- package/neural-interface/public/index.html +2 -2
- package/neural-interface/public/shared/account-menu.js +4 -1
- package/neural-interface/public/shared/account-usage.js +62 -2
- package/neural-interface/public/shared/account-view.js +2 -1
- package/neural-interface/public/shared/agent-runtime-options.js +55 -11
- package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +172 -17
- package/neural-interface/public/shared/assistant/asst-api.js +18 -3
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +424 -152
- package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
- package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
- package/neural-interface/public/shared/assistant/asst-menu.js +275 -77
- package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +236 -35
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +28 -10
- package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
- package/neural-interface/public/shared/assistant/asst-render.js +382 -36
- package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
- package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
- package/neural-interface/public/shared/assistant/asst-scenes.js +528 -0
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
- package/neural-interface/public/shared/assistant/asst-state.js +14 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +109 -17
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +13 -1
- package/neural-interface/public/shared/cc-dropdown.js +480 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
- package/neural-interface/public/shared/cdx/cdx-render.js +15 -3
- package/neural-interface/public/shared/cdx/cdx-styles.js +49 -5
- package/neural-interface/public/shared/cdx/cdx-tabs.js +152 -35
- package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
- package/neural-interface/public/shared/data-cache.js +223 -0
- package/neural-interface/public/shared/desktop-bridge.js +11 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
- package/neural-interface/public/shared/desktop-titlebar.js +214 -28
- package/neural-interface/public/shared/desktop-update-view.js +15 -5
- package/neural-interface/public/shared/file-links.js +73 -8
- package/neural-interface/public/shared/html-shell.js +74 -80
- package/neural-interface/public/shared/launch-prefetch.js +59 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +11 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +97 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +3 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +16 -9
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +60 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +83 -5
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +195 -19
- package/neural-interface/public/shared/opencode-providers.js +39 -0
- package/neural-interface/public/shared/schedules-view.js +318 -0
- package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
- package/neural-interface/public/shared/settings/settings-app.js +243 -0
- package/neural-interface/public/shared/settings/settings-data.js +88 -0
- package/neural-interface/public/shared/settings/settings-ia.js +17 -0
- package/neural-interface/public/shared/showcase-privacy.js +2 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
- package/neural-interface/public/shared/styles.css +543 -372
- package/neural-interface/public/shared/ui-assistant.js +27 -5
- package/neural-interface/public/shared/ui-automation-studio.js +67 -27
- package/neural-interface/public/shared/ui-bookmarks.js +2 -1
- package/neural-interface/public/shared/ui-claude-panel.js +126 -12
- package/neural-interface/public/shared/ui-codex-panel.js +1 -0
- package/neural-interface/public/shared/ui-detail.js +6 -6
- package/neural-interface/public/shared/ui-explorer.js +2 -0
- package/neural-interface/public/shared/ui-file-links.js +1 -0
- package/neural-interface/public/shared/ui-image-gallery.js +6 -5
- package/neural-interface/public/shared/ui-invite.js +18 -2
- package/neural-interface/public/shared/ui-link.js +9 -9
- package/neural-interface/public/shared/ui-menubar.js +232 -33
- package/neural-interface/public/shared/ui-native-loop-router.js +2 -0
- package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
- package/neural-interface/public/shared/ui-schedules-studio.js +2099 -869
- package/neural-interface/public/shared/ui-sessions.js +15 -4
- package/neural-interface/public/shared/ui-settings.js +87 -36
- package/neural-interface/public/shared/ui-sidebar.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +55 -4
- package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
- package/neural-interface/public/shared/ui-skills.js +70 -12
- package/neural-interface/public/shared/ui-sync.js +9 -0
- package/neural-interface/public/shared/ui-terminal.js +36 -20
- package/neural-interface/public/shared/ui-trash.js +8 -1
- package/neural-interface/public/shared/ui-tutorial-steps.js +7 -3
- package/neural-interface/public/shared/ui-tutorial.js +15 -5
- package/neural-interface/public/shared/ui-update.js +87 -16
- package/neural-interface/public/shared/ui-whiteboard.js +2 -1
- package/neural-interface/public/variant/2d/main.js +4 -1
- package/neural-interface/public/variant/3d/main.js +3 -0
- package/neural-interface/public/vendor/highlight/LICENSE +29 -0
- package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
- package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
- package/neural-interface/public/vendor/highlight/manifest.json +5 -0
- package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
- package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
- package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
- package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
- package/neural-interface/public/vendor/marked/manifest.json +4 -0
- package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
- package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
- package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
- package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
- package/neural-interface/scripts/build-vendor.mjs +84 -3
- package/neural-interface/scripts/rebuild-pty.js +14 -2
- package/neural-interface/server.js +1279 -372
- package/npm-shrinkwrap.json +4424 -0
- package/package.json +54 -2
- package/postinstall.js +61 -47
- package/preuninstall.js +8 -35
- package/setup.js +76 -44
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/schedule.md +2 -2
- package/updater.cmd +2 -2
- package/updater.mjs +97 -3
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun — /api/system/app: Settings > App for an npm or Git install
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// GET /api/system/app { ok, install, desktop, updates: { auto }, startup: { … } }
|
|
6
|
+
// PUT /api/system/app/updates { auto: boolean } the automatic update check and its badge
|
|
7
|
+
// PUT /api/system/app/startup { enabled: boolean } start the server at login (lib/login-start.js)
|
|
8
|
+
// POST /api/system/app/open-folder { which: 'data' | 'logs' }
|
|
9
|
+
//
|
|
10
|
+
// Automatic updates here are the check: on (the default), the server looks for
|
|
11
|
+
// a newer version when it starts and the page shows the update badge and asks
|
|
12
|
+
// again every half hour; off, it looks only when the user asks (Check for
|
|
13
|
+
// updates). Installing always goes through the update dialog and its backup
|
|
14
|
+
// step; nothing installs unattended. The setting is DATA_HOME/data/app-settings.json.
|
|
15
|
+
//
|
|
16
|
+
// In the desktop app (`desktop: true`) the app's own settings are the
|
|
17
|
+
// window's (packaging/desktop/src/app-prefs.mjs, through the preload bridge):
|
|
18
|
+
// this answers what it is and changes nothing.
|
|
19
|
+
//
|
|
20
|
+
// These are the owner's acts on this computer (a login entry, a folder opened
|
|
21
|
+
// on its screen), so the routes take the guards of /api/cli/install
|
|
22
|
+
// (lib/http-guards.js): a loopback socket and no proxy header, a localhost
|
|
23
|
+
// Host, no invite guest; a write also its own Origin, a JSON body,
|
|
24
|
+
// X-SynaBun-UI: 1 and no agent header. (`/api/system` is admin-only in
|
|
25
|
+
// lib/request-gate.js too.) A request names a switch, never a path or a command.
|
|
26
|
+
|
|
27
|
+
import { Router } from 'express';
|
|
28
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
29
|
+
import { dirname } from 'node:path';
|
|
30
|
+
import { guestForbidden, noStore, requireLocal, requireLocalHost, requireSameOriginJson, sendGuardError, uiOnly } from './http-guards.js';
|
|
31
|
+
import { runProgram } from './file-explorer-system.js';
|
|
32
|
+
|
|
33
|
+
export const APP_SETTINGS_DEFAULTS = Object.freeze({ autoUpdateCheck: true });
|
|
34
|
+
|
|
35
|
+
export function normalizeAppSettings(raw) {
|
|
36
|
+
const source = raw && typeof raw === 'object' && !Array.isArray(raw) ? raw : {};
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const [key, value] of Object.entries(APP_SETTINGS_DEFAULTS)) out[key] = typeof source[key] === typeof value ? source[key] : value;
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** DATA_HOME/data/app-settings.json, read once and written through (this server is its only writer). */
|
|
43
|
+
export function createAppSettingsStore({ path, fs = { mkdirSync, readFileSync, renameSync, writeFileSync } } = {}) {
|
|
44
|
+
let current = null;
|
|
45
|
+
const load = () => {
|
|
46
|
+
if (current) return current;
|
|
47
|
+
try { current = normalizeAppSettings(JSON.parse(fs.readFileSync(path, 'utf8'))); } catch { current = normalizeAppSettings(null); }
|
|
48
|
+
return current;
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
get: () => ({ ...load() }),
|
|
52
|
+
set(patch) {
|
|
53
|
+
const next = normalizeAppSettings({ ...load(), ...patch });
|
|
54
|
+
fs.mkdirSync(dirname(path), { recursive: true });
|
|
55
|
+
const temp = `${path}.${process.pid}.tmp`;
|
|
56
|
+
fs.writeFileSync(temp, JSON.stringify(next, null, 2) + '\n');
|
|
57
|
+
fs.renameSync(temp, path);
|
|
58
|
+
current = next;
|
|
59
|
+
return { ...next };
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The program that shows a folder in the system's file manager (no shell; the path is an argument of its own). */
|
|
65
|
+
export function openFolderCommand(platform, path, { env = process.env } = {}) {
|
|
66
|
+
if (platform === 'darwin') return { file: 'open', args: [path] };
|
|
67
|
+
// explorer.exe answers 1 even when it opened the folder.
|
|
68
|
+
if (platform === 'win32') return { file: 'explorer.exe', args: [path], anyExit: true };
|
|
69
|
+
if (!env.DISPLAY && !env.WAYLAND_DISPLAY) return null;
|
|
70
|
+
return { file: 'xdg-open', args: [path] };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Open a folder on this computer's screen; resolves true when the file manager took it. */
|
|
74
|
+
export async function openFolderOnScreen(path, { platform = process.platform, env = process.env, run = runProgram, mkdir = mkdirSync } = {}) {
|
|
75
|
+
const command = openFolderCommand(platform, path, { env });
|
|
76
|
+
if (!command) return false;
|
|
77
|
+
try { mkdir(path, { recursive: true }); } catch {}
|
|
78
|
+
try {
|
|
79
|
+
await run(command.file, command.args);
|
|
80
|
+
return true;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return !!command.anyExit && error?.code !== 'ENOENT';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function localRouter({ isGuestRequest, port }) {
|
|
87
|
+
const router = Router();
|
|
88
|
+
const portOf = () => Number(typeof port === 'function' ? port() : port);
|
|
89
|
+
const built = new Map();
|
|
90
|
+
const perPort = (name, factory) => (req, res, next) => {
|
|
91
|
+
const key = `${name}:${portOf()}`;
|
|
92
|
+
let guard = built.get(key);
|
|
93
|
+
if (!guard) {
|
|
94
|
+
try { guard = factory(portOf()); } catch { return sendGuardError(res, 500, 'BAD_PORT', 'The app settings have no valid port.'); }
|
|
95
|
+
built.set(key, guard);
|
|
96
|
+
}
|
|
97
|
+
return guard(req, res, next);
|
|
98
|
+
};
|
|
99
|
+
router.use(noStore, guestForbidden(isGuestRequest), requireLocal, perPort('host', requireLocalHost));
|
|
100
|
+
return { router, mutating: [perPort('origin', requireSameOriginJson), uiOnly] };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {object} deps
|
|
105
|
+
* @param {ReturnType<typeof createAppSettingsStore>} deps.store
|
|
106
|
+
* @param {ReturnType<import('./login-start.js').createLoginStart>} deps.loginStart
|
|
107
|
+
* @param {{ data: string, logs: string }} deps.folders
|
|
108
|
+
* @param {string} deps.install 'npm' | 'git' | 'app' | 'desktop'
|
|
109
|
+
* @param {(auto: boolean) => void} [deps.onAutoUpdateChange]
|
|
110
|
+
* @param {(path: string) => Promise<boolean>} [deps.openFolder]
|
|
111
|
+
*/
|
|
112
|
+
export function createAppControlsApi({
|
|
113
|
+
store, loginStart, folders, install = 'npm', onAutoUpdateChange = () => {}, openFolder = (path) => openFolderOnScreen(path),
|
|
114
|
+
isGuestRequest = () => false, port = 3344, log = () => {},
|
|
115
|
+
} = {}) {
|
|
116
|
+
if (!store || !loginStart || !folders) throw new Error('createAppControlsApi needs the settings store, the login start and the folders');
|
|
117
|
+
const { router, mutating } = localRouter({ isGuestRequest, port });
|
|
118
|
+
const desktop = install === 'desktop';
|
|
119
|
+
|
|
120
|
+
const body = () => {
|
|
121
|
+
let startup;
|
|
122
|
+
try { startup = loginStart.status(); } catch (error) { startup = { supported: false, reason: 'unsupported', enabled: false, state: 'off', owner: null, error: String(error?.message || error) }; }
|
|
123
|
+
return { ok: true, install, desktop, updates: { auto: store.get().autoUpdateCheck !== false }, startup };
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
router.get('/', (req, res) => { res.json(body()); });
|
|
127
|
+
|
|
128
|
+
router.put('/updates', ...mutating, (req, res) => {
|
|
129
|
+
const auto = req.body?.auto;
|
|
130
|
+
if (typeof auto !== 'boolean') return res.status(400).json({ ok: false, code: 'BAD_REQUEST', error: 'Send { auto: true | false }.' });
|
|
131
|
+
if (desktop) return res.status(409).json({ ok: false, code: 'DESKTOP_MANAGED', error: 'The SynaBun app keeps this setting itself.' });
|
|
132
|
+
try { store.set({ autoUpdateCheck: auto }); } catch (error) {
|
|
133
|
+
return res.status(500).json({ ok: false, code: 'SAVE_FAILED', error: String(error?.message || error) });
|
|
134
|
+
}
|
|
135
|
+
try { onAutoUpdateChange(auto); } catch {}
|
|
136
|
+
res.json(body());
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
router.put('/startup', ...mutating, async (req, res) => {
|
|
140
|
+
const enabled = req.body?.enabled;
|
|
141
|
+
if (typeof enabled !== 'boolean') return res.status(400).json({ ok: false, code: 'BAD_REQUEST', error: 'Send { enabled: true | false }.' });
|
|
142
|
+
try {
|
|
143
|
+
await loginStart.set(enabled);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const code = String(error?.code || 'refused');
|
|
146
|
+
log(`[app] start at login could not be turned ${enabled ? 'on' : 'off'}: ${error?.message || error}`);
|
|
147
|
+
return res.status(409).json({ ...body(), ok: false, code, error: String(error?.message || error).slice(0, 300) });
|
|
148
|
+
}
|
|
149
|
+
res.json(body());
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
router.post('/open-folder', ...mutating, async (req, res) => {
|
|
153
|
+
const which = req.body?.which;
|
|
154
|
+
if (which !== 'data' && which !== 'logs') return res.status(400).json({ ok: false, code: 'BAD_REQUEST', error: "Send { which: 'data' | 'logs' }." });
|
|
155
|
+
const opened = await openFolder(folders[which]).catch(() => false);
|
|
156
|
+
if (!opened) return res.status(422).json({ ok: false, code: 'NO_FILE_MANAGER', error: 'No file manager opened the folder.' });
|
|
157
|
+
res.json({ ok: true });
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return router;
|
|
161
|
+
}
|
|
@@ -17,6 +17,7 @@ import { tooLargeMessage } from './assistant-attachments.js';
|
|
|
17
17
|
import { getSessionPolicy, requiresHumanApproval } from './remote-policy.js';
|
|
18
18
|
import { clampPlanDispatch, PLAN_DISPATCH_NOTE, runCanChangeCode } from './assistant-plan-permissions.js';
|
|
19
19
|
import { noStore, requireJson, requireLocal, requireLocalHost, sendGuardError } from './http-guards.js';
|
|
20
|
+
import { setUntrustedFileHeaders } from './untrusted-file-headers.js';
|
|
20
21
|
|
|
21
22
|
/** The header whatsapp_send_file's caller proves it is the WhatsApp conversation's own brain with (assistant-runtime.js BRAIN_CREDENTIAL_RE). */
|
|
22
23
|
export const BRAIN_CREDENTIAL_HEADER = 'x-synabun-brain-token';
|
|
@@ -150,6 +151,8 @@ export function createAssistantApi({
|
|
|
150
151
|
// after this router), and the port the server listens on (the Host check of that route).
|
|
151
152
|
whatsapp = null,
|
|
152
153
|
port = 3344,
|
|
154
|
+
// The Admin sandbox setting (lib/agent-sandbox-setting.js): one global value for every agent SynaBun starts.
|
|
155
|
+
adminSandbox = null,
|
|
153
156
|
} = {}) {
|
|
154
157
|
if (!dispatcher) throw new Error('createAssistantApi requires a dispatcher');
|
|
155
158
|
const router = Router();
|
|
@@ -299,7 +302,10 @@ export function createAssistantApi({
|
|
|
299
302
|
// ── catalog ────────────────────────────────────────────────────────────────
|
|
300
303
|
router.get('/catalog', async (req, res) => {
|
|
301
304
|
try {
|
|
302
|
-
|
|
305
|
+
// Quotas are shown as last read and read again in the background (the probe can take seconds; the
|
|
306
|
+
// dispatcher and the router read them fresh before they choose an account).
|
|
307
|
+
accountUsage?.refresh()?.catch?.(() => {});
|
|
308
|
+
const usage = () => accountUsage?.list({ stale: true });
|
|
303
309
|
// refresh=1 rebuilds from the providers' endpoints; deep=1 also makes them ask the CLI / app-server again.
|
|
304
310
|
const deep = req.query.deep === '1';
|
|
305
311
|
const force = req.query.refresh === '1' || deep;
|
|
@@ -318,8 +324,8 @@ export function createAssistantApi({
|
|
|
318
324
|
// names none.
|
|
319
325
|
const accounts = {
|
|
320
326
|
...(brief.accounts || {}),
|
|
321
|
-
...(claudeAccounts ? { 'claude-code':
|
|
322
|
-
...(codexAccounts ? { codex:
|
|
327
|
+
...(claudeAccounts ? { 'claude-code': usage()?.['claude-code'] || claudeAccounts.listForClient() } : {}),
|
|
328
|
+
...(codexAccounts ? { codex: usage()?.codex || codexAccounts.listForClient() } : {}),
|
|
323
329
|
};
|
|
324
330
|
return res.json({ ok: true, generatedAt: new Date(now()).toISOString(), ...brief, accounts, claudeWorkerAccount: claudeWorkerAccountId(), codexWorkerAccount: codexWorkerAccountId(), limits: dispatcher.limits, runs: dispatcher.list({ activeOnly: true }), totals: dispatcher.totals() });
|
|
325
331
|
}
|
|
@@ -327,7 +333,7 @@ export function createAssistantApi({
|
|
|
327
333
|
// within seconds), so it is asked every time: this answer is reused only while it hands back the
|
|
328
334
|
// build the answer was made from (a rebuild or invalidate() gives another one).
|
|
329
335
|
const base = typeof buildCatalog === 'function' ? await buildCatalog({ force, deep }) : NO_CATALOG;
|
|
330
|
-
if (!force && catalogCache && catalogCache.base === base && now() - catalogCache.at < 30_000) return res.json(accountUsage ? { ...catalogCache.value, accounts: { ...catalogCache.value.accounts, ...
|
|
336
|
+
if (!force && catalogCache && catalogCache.base === base && now() - catalogCache.at < 30_000) return res.json(accountUsage ? { ...catalogCache.value, accounts: { ...catalogCache.value.accounts, ...usage() } } : catalogCache.value);
|
|
331
337
|
// Hidden rows stay server-side (view=manage lists them); the lists themselves ship.
|
|
332
338
|
const { hiddenRows: _hiddenRows, ...visible } = base || {};
|
|
333
339
|
const value = {
|
|
@@ -336,8 +342,8 @@ export function createAssistantApi({
|
|
|
336
342
|
...visible,
|
|
337
343
|
accounts: {
|
|
338
344
|
...(base.accounts || {}),
|
|
339
|
-
'claude-code':
|
|
340
|
-
codex:
|
|
345
|
+
'claude-code': usage()?.['claude-code'] || (claudeAccounts ? claudeAccounts.listForClient() : (base.accounts?.['claude-code'] || [])),
|
|
346
|
+
codex: usage()?.codex || (codexAccounts ? codexAccounts.listForClient() : (base.accounts?.codex || [])),
|
|
341
347
|
},
|
|
342
348
|
claudeWorkerAccount: claudeWorkerAccountId(),
|
|
343
349
|
codexWorkerAccount: codexWorkerAccountId(),
|
|
@@ -834,6 +840,8 @@ export function createAssistantApi({
|
|
|
834
840
|
'Accept-Ranges': 'bytes', 'X-Content-Type-Options': 'nosniff', 'Cache-Control': 'private, max-age=3600',
|
|
835
841
|
});
|
|
836
842
|
if (range) res.set('Content-Range', `bytes ${start}-${end}/${size}`);
|
|
843
|
+
// A run's media is what a worker made: opened by its address it runs nothing, and an SVG is a download.
|
|
844
|
+
setUntrustedFileHeaders(res, file.path, file.mime);
|
|
837
845
|
if (req.method === 'HEAD' || !size) { await handle.close().catch(() => {}); return res.end(); }
|
|
838
846
|
const stream = handle.createReadStream({ start, end });
|
|
839
847
|
stream.on('error', () => res.destroy());
|
|
@@ -994,9 +1002,17 @@ export function createAssistantApi({
|
|
|
994
1002
|
if (!requireRuntime(res)) return;
|
|
995
1003
|
try { res.json({ ok: true, session: await runtime.closeSession(req.params.id) }); } catch (error) { sendError(res, error); }
|
|
996
1004
|
});
|
|
1005
|
+
// Remove (the Sessions menu): the record, its transcript and its usage journal go for good, after its
|
|
1006
|
+
// running turn and the workers it dispatched are stopped. Memories made from it stay.
|
|
997
1007
|
router.delete('/sessions/:id', async (req, res) => {
|
|
998
1008
|
if (!requireRuntime(res)) return;
|
|
999
|
-
try {
|
|
1009
|
+
try {
|
|
1010
|
+
if (runtime.removeSession) {
|
|
1011
|
+
const { removed, stoppedRuns } = await runtime.removeSession(req.params.id);
|
|
1012
|
+
return res.json({ ok: true, removed, stoppedRuns });
|
|
1013
|
+
}
|
|
1014
|
+
res.json({ ok: true, removed: await runtime.destroySession(req.params.id) });
|
|
1015
|
+
} catch (error) { sendError(res, error); }
|
|
1000
1016
|
});
|
|
1001
1017
|
|
|
1002
1018
|
// ── attachments ────────────────────────────────────────────────────────────
|
|
@@ -1230,6 +1246,23 @@ export function createAssistantApi({
|
|
|
1230
1246
|
} catch (error) { sendError(res, error); }
|
|
1231
1247
|
});
|
|
1232
1248
|
|
|
1249
|
+
// ── Admin sandbox (lib/agent-sandbox-setting.js) ───────────────────────────
|
|
1250
|
+
// Whether an agent SynaBun starts may use a sandbox whose setup needs Windows administrator
|
|
1251
|
+
// approval. Global, not per session. The user's setting: an agent cannot change it.
|
|
1252
|
+
router.get('/admin-sandbox', (req, res) => {
|
|
1253
|
+
if (!adminSandbox) return res.status(503).json({ ok: false, code: 'ADMIN_SANDBOX_UNAVAILABLE', error: 'The Admin sandbox setting is not available on this server' });
|
|
1254
|
+
res.json({ ok: true, ...adminSandbox.view() });
|
|
1255
|
+
});
|
|
1256
|
+
router.put('/admin-sandbox', requireJson, (req, res) => {
|
|
1257
|
+
if (!adminSandbox) return res.status(503).json({ ok: false, code: 'ADMIN_SANDBOX_UNAVAILABLE', error: 'The Admin sandbox setting is not available on this server' });
|
|
1258
|
+
if (callerContext(req).kind !== 'ui' || req.get('x-synabun-desktop-grant')) {
|
|
1259
|
+
return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Only the user can change the Admin sandbox.' });
|
|
1260
|
+
}
|
|
1261
|
+
const value = req.body?.adminSandbox;
|
|
1262
|
+
if (typeof value !== 'boolean') return res.status(400).json({ ok: false, code: 'INVALID_VALUE', error: 'adminSandbox must be true or false.' });
|
|
1263
|
+
try { res.json({ ok: true, ...adminSandbox.set(value) }); } catch (error) { sendError(res, error); }
|
|
1264
|
+
});
|
|
1265
|
+
|
|
1233
1266
|
// ── sidepanel tab registry (UI heartbeat) ──────────────────────────────────
|
|
1234
1267
|
function listTabs() {
|
|
1235
1268
|
const cutoff = now() - tabTtlMs;
|
|
@@ -36,6 +36,12 @@ export function opencodeImageParts(images = []) {
|
|
|
36
36
|
|
|
37
37
|
/** The route-gate plugin (opencode-route-gate.js) as a file URL the serve can load. */
|
|
38
38
|
export const OPENCODE_ROUTE_GATE_PLUGIN = pathToFileURL(resolve(dirname(fileURLToPath(import.meta.url)), 'opencode-route-gate.js')).href;
|
|
39
|
+
/**
|
|
40
|
+
* The same gate for OpenCode 2 (opencode2-route-gate/: OpenCode 2 loads a plugin
|
|
41
|
+
* folder from the config's `plugins` list). Both entries are written: OpenCode 1
|
|
42
|
+
* ignores `plugins`, OpenCode 2 skips the 1.x file (a warning in its log).
|
|
43
|
+
*/
|
|
44
|
+
export const OPENCODE2_ROUTE_GATE_PLUGIN = pathToFileURL(resolve(dirname(fileURLToPath(import.meta.url)), 'opencode2-route-gate')).href;
|
|
39
45
|
|
|
40
46
|
/**
|
|
41
47
|
* SynaBun's plan agent. OpenCode adds its own read-only reminder ("commands may
|
|
@@ -96,9 +102,15 @@ export function patchOpenCodeAssistantConfig(xdgRoot, { persona = '', role = 'as
|
|
|
96
102
|
// read-only classifier decides on the whole line.
|
|
97
103
|
config.agent.plan = { ...(config.agent.plan || {}), permission: { ...(config.agent.plan?.permission || {}), bash: 'ask' } };
|
|
98
104
|
const plugins = (Array.isArray(config.plugin) ? config.plugin : []).filter((entry) => (Array.isArray(entry) ? entry[0] : entry) !== OPENCODE_ROUTE_GATE_PLUGIN);
|
|
105
|
+
const plugins2 = (Array.isArray(config.plugins) ? config.plugins : []).filter((entry) => (entry && typeof entry === 'object' ? entry.package : entry) !== OPENCODE2_ROUTE_GATE_PLUGIN);
|
|
99
106
|
// `remote`: a WhatsApp session's serve — the plugin refuses a call it could not check (fail closed).
|
|
100
|
-
if (routeGate?.url && routeGate?.session && routeGate?.token)
|
|
107
|
+
if (routeGate?.url && routeGate?.session && routeGate?.token) {
|
|
108
|
+
const options = { url: routeGate.url, session: routeGate.session, token: routeGate.token, ...(routeGate.remote === true ? { remote: true } : {}) };
|
|
109
|
+
plugins.push([OPENCODE_ROUTE_GATE_PLUGIN, options]);
|
|
110
|
+
plugins2.push({ package: OPENCODE2_ROUTE_GATE_PLUGIN, options });
|
|
111
|
+
}
|
|
101
112
|
if (plugins.length) config.plugin = plugins; else delete config.plugin;
|
|
113
|
+
if (plugins2.length) config.plugins = plugins2; else delete config.plugins;
|
|
102
114
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
103
115
|
// It carries this brain's grants (the desktop grant): readable by this user only.
|
|
104
116
|
try { chmodSync(configPath, 0o600); } catch { /* a filesystem without modes */ }
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// SynaBun — an Assistant task run's browser policy as an OpenCode 2 plugin.
|
|
2
|
+
// The same policy as ../opencode-browser-policy.js (read that file for what it
|
|
3
|
+
// does and why), for OpenCode 2's plugin API: a directory OpenCode loads from
|
|
4
|
+
// the config's `plugins` entry, whose default export is `{ id, setup(context) }`
|
|
5
|
+
// with a hook on `tool.execute.before`; a refusal is thrown. OpenCode 2 calls
|
|
6
|
+
// the shell tool `shell` and the sub-agent tool `subagent`: the policy, written
|
|
7
|
+
// for OpenCode 1's names, is asked with `bash` and `task`.
|
|
8
|
+
|
|
9
|
+
// An absolute file URL: the policy loads from SynaBun's lib/ wherever OpenCode imports this file from.
|
|
10
|
+
const POLICY_URL = new URL('../../browser-tool-policy.js', import.meta.url).href;
|
|
11
|
+
const V1_NAMES = { shell: 'bash', subagent: 'task' };
|
|
12
|
+
|
|
13
|
+
// OpenCode's own tools that run no command and fetch no page (OpenCode 1's names).
|
|
14
|
+
const OWN_TOOLS = new Set([
|
|
15
|
+
'read', 'list', 'glob', 'grep', 'edit', 'write', 'patch', 'multiedit', 'apply_patch', 'todowrite', 'todoread',
|
|
16
|
+
'task', 'skill', 'lsp', 'codesearch', 'question', 'batch', 'invalid', 'plan_exit', 'plan_enter',
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
id: 'synabun.browser-policy',
|
|
21
|
+
async setup(context) {
|
|
22
|
+
const options = context?.options || {};
|
|
23
|
+
let policy = null;
|
|
24
|
+
let loadError = null;
|
|
25
|
+
try { policy = await import(POLICY_URL); } catch (error) { loadError = error; }
|
|
26
|
+
const cwd = (typeof options.cwd === 'string' && options.cwd) || context?.location?.directory || process.cwd();
|
|
27
|
+
if (typeof policy?.browserToolDenial !== 'function') {
|
|
28
|
+
const why = loadError?.message || 'browserToolDenial is missing';
|
|
29
|
+
console.error(`[SynaBun] browser policy could not load from ${POLICY_URL} (${why}); refusing bash, webfetch, websearch and other servers' tools for this run`);
|
|
30
|
+
await context.tool.hook('execute.before', async (call) => {
|
|
31
|
+
const raw = String(call?.tool || '');
|
|
32
|
+
const tool = V1_NAMES[raw] || raw;
|
|
33
|
+
if (/^synabun_/i.test(tool) || OWN_TOOLS.has(tool.toLowerCase())) return;
|
|
34
|
+
throw new Error(`SynaBun browser policy: ${tool} was refused — SynaBun's browser policy could not load (${why}), so no command, web tool or other server's tool can be checked for a browser launch. SynaBun's own tools (SynaBun_*) and file tools still work. Stop and report this.`);
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
await context.tool.hook('execute.before', async (call) => {
|
|
39
|
+
const raw = String(call?.tool || '');
|
|
40
|
+
const reason = policy.browserToolDenial(V1_NAMES[raw] || raw, call?.input || {}, { host: 'opencode', cwd });
|
|
41
|
+
if (reason) throw new Error(reason);
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// SynaBun — the Assistant's route gate as an OpenCode 2 plugin.
|
|
2
|
+
// The same gate as ../opencode-route-gate.js (read that file for what it does
|
|
3
|
+
// and why), for OpenCode 2's plugin API: a directory OpenCode loads from the
|
|
4
|
+
// config's `plugins` entry ({ package: <this folder as a file URL>, options }),
|
|
5
|
+
// whose default export is `{ id, setup(context) }`; a hook on
|
|
6
|
+
// `tool.execute.before` sees `{ tool, sessionID, input }` and refuses a call by
|
|
7
|
+
// throwing (OpenCode records the message as that tool's error). OpenCode 2
|
|
8
|
+
// calls two tools by other names than OpenCode 1 (`shell` for bash,
|
|
9
|
+
// `subagent` for task): the gate is asked with OpenCode 1's names, which the
|
|
10
|
+
// rest of SynaBun knows. OpenCode 1 never reads this folder (it loads the
|
|
11
|
+
// `plugin` entry), and OpenCode 2 never loads the 1.x file.
|
|
12
|
+
|
|
13
|
+
const MAX_INPUT_BYTES = 256 * 1024;
|
|
14
|
+
const UNCHECKED = 'SynaBun could not check this call, so a WhatsApp session does not run it.';
|
|
15
|
+
const V1_NAMES = { shell: 'bash', subagent: 'task' };
|
|
16
|
+
|
|
17
|
+
/** { input, inputComplete }: the whole arguments when small enough to send, else their command/action. */
|
|
18
|
+
function callArguments(args) {
|
|
19
|
+
if (!args || typeof args !== 'object' || Array.isArray(args)) return { input: {}, inputComplete: false };
|
|
20
|
+
let size = Infinity;
|
|
21
|
+
try { size = JSON.stringify(args).length; } catch {}
|
|
22
|
+
if (size <= MAX_INPUT_BYTES) return { input: args, inputComplete: true };
|
|
23
|
+
const subset = {};
|
|
24
|
+
for (const key of ['command', 'action']) {
|
|
25
|
+
if (typeof args[key] === 'string' && args[key].length <= 64 * 1024) subset[key] = args[key];
|
|
26
|
+
}
|
|
27
|
+
return { input: subset, inputComplete: false };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default {
|
|
31
|
+
id: 'synabun.route-gate',
|
|
32
|
+
async setup(context) {
|
|
33
|
+
const { url, session, token, remote = false } = context?.options || {};
|
|
34
|
+
if (!url || !session || !token) return;
|
|
35
|
+
const post = (body) => fetch(url, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: { 'content-type': 'application/json' },
|
|
38
|
+
body: JSON.stringify({ session, token, host: 'opencode', ...body }),
|
|
39
|
+
signal: AbortSignal.timeout(5000),
|
|
40
|
+
});
|
|
41
|
+
// Never blocks loading; a lost hello only keeps the reactive fallback on.
|
|
42
|
+
post({ hello: true }).catch(() => {});
|
|
43
|
+
await context.tool.hook('execute.before', async (call) => {
|
|
44
|
+
const name = String(call?.tool || '');
|
|
45
|
+
let decision = null;
|
|
46
|
+
try {
|
|
47
|
+
const response = await post({ tool: V1_NAMES[name] || name, providerSessionId: call?.sessionID || null, ...callArguments(call?.input) });
|
|
48
|
+
decision = await response.json();
|
|
49
|
+
} catch {
|
|
50
|
+
if (remote === true) throw new Error(UNCHECKED);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (decision && decision.allow === false) throw new Error(String(decision.reason || 'SynaBun route gate: call agent_route first.'));
|
|
54
|
+
if (remote === true && !(decision && decision.allow === true)) throw new Error(UNCHECKED);
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
};
|
|
@@ -605,6 +605,8 @@ export function compactRow(row) {
|
|
|
605
605
|
* @param {(change:{providers:string[], generatedAt:string})=>void} [deps.onChange] a build listed other
|
|
606
606
|
* models than the build before it (hiding has its own event and does not count)
|
|
607
607
|
* @param {number} [deps.retryMs] how soon a build is redone while Claude Code or Codex gave no verified list
|
|
608
|
+
* @param {boolean} [deps.staleWhileRevalidate] an old build (past its lifetime) is given at once and built
|
|
609
|
+
* again in the background, one build at a time; only a first build, invalidate() and force wait for one
|
|
608
610
|
*/
|
|
609
611
|
export function createAssistantCatalog({
|
|
610
612
|
fetchJson,
|
|
@@ -617,6 +619,7 @@ export function createAssistantCatalog({
|
|
|
617
619
|
onChange = null,
|
|
618
620
|
ttlMs = 30_000,
|
|
619
621
|
retryMs = 5_000,
|
|
622
|
+
staleWhileRevalidate = false,
|
|
620
623
|
now = Date.now,
|
|
621
624
|
log = () => {},
|
|
622
625
|
} = {}) {
|
|
@@ -753,7 +756,17 @@ export function createAssistantCatalog({
|
|
|
753
756
|
*/
|
|
754
757
|
async function full({ force = false, deep = false } = {}) {
|
|
755
758
|
if (!force && !deep && cache && now() - cache.at < cache.ttl) return cache.value;
|
|
759
|
+
// Past its lifetime: with staleWhileRevalidate the old build is the answer while the next one is made
|
|
760
|
+
// (never a build that began before an invalidate(): `current` says it was made for the lists as they are).
|
|
761
|
+
if (staleWhileRevalidate && !force && !deep && cache?.current) {
|
|
762
|
+
if (!inflight) startBuild({ deep: false }).catch((error) => log('catalog:background-error', error?.message || String(error)));
|
|
763
|
+
return cache.value;
|
|
764
|
+
}
|
|
756
765
|
if (inflight) return inflight;
|
|
766
|
+
return startBuild({ deep });
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function startBuild({ deep = false } = {}) {
|
|
757
770
|
const started = epoch;
|
|
758
771
|
inflight = build({ deep }).then((built) => {
|
|
759
772
|
const { changedProviders, ...value } = built;
|
|
@@ -762,7 +775,7 @@ export function createAssistantCatalog({
|
|
|
762
775
|
const degraded = ['claude-code', 'codex'].some((p) => value.sources[p] !== 'live');
|
|
763
776
|
degradedBuilds = degraded ? degradedBuilds + 1 : 0;
|
|
764
777
|
const ttl = degraded ? Math.min(ttlMs, retryMs * 2 ** (degradedBuilds - 1)) : ttlMs;
|
|
765
|
-
cache = { at: now(), value, ttl: started === epoch ? ttl : 0 };
|
|
778
|
+
cache = { at: now(), value, ttl: started === epoch ? ttl : 0, current: started === epoch };
|
|
766
779
|
if (changedProviders.length && typeof onChange === 'function') {
|
|
767
780
|
try { onChange({ providers: changedProviders, generatedAt: value.generatedAt }); } catch (error) { log('catalog:onchange-error', error?.message || String(error)); }
|
|
768
781
|
}
|
|
@@ -872,6 +885,8 @@ export function createAssistantCatalog({
|
|
|
872
885
|
}
|
|
873
886
|
|
|
874
887
|
function invalidate() { cache = null; epoch += 1; }
|
|
888
|
+
/** The lists may have changed (a serve restarted, a CLI updated): the next read still answers with this build and makes the next one. */
|
|
889
|
+
function markStale() { if (cache) cache = { ...cache, at: -Infinity }; }
|
|
875
890
|
/** A provider's own list changed: drop the copy and build the next one now (onChange reports the difference). */
|
|
876
891
|
function refresh() {
|
|
877
892
|
invalidate();
|
|
@@ -879,5 +894,5 @@ export function createAssistantCatalog({
|
|
|
879
894
|
return pending ? pending.catch(() => null).then(() => full()) : full();
|
|
880
895
|
}
|
|
881
896
|
|
|
882
|
-
return { get, full, peek, find, hiddenId, manageRows, manageSnapshot, brainInfo, invalidate, refresh };
|
|
897
|
+
return { get, full, peek, find, hiddenId, manageRows, manageSnapshot, brainInfo, invalidate, markStale, refresh };
|
|
883
898
|
}
|
|
@@ -1894,6 +1894,9 @@ export function createAssistantDispatcher({
|
|
|
1894
1894
|
liveState.turnTokens = null;
|
|
1895
1895
|
entry.turns = [...(entry.turns || []), turn].slice(-50);
|
|
1896
1896
|
entry.lastResult = parsed;
|
|
1897
|
+
// The run's own descriptor (lib/native-loop-runtime.js) carries it too, for the surfaces that read that one
|
|
1898
|
+
// (the OpenCode panel's pill): a warm worker whose turn ended asking waits on the user, it does not rest.
|
|
1899
|
+
ctx.setOutcome?.(parsed.status || null);
|
|
1897
1900
|
entry.lastText = text.slice(0, 20_000);
|
|
1898
1901
|
// The turn has ended: its result is in. Idle time from here on is not work.
|
|
1899
1902
|
pauseWork(entry, liveState);
|
|
@@ -449,7 +449,7 @@ export function opencodeControlResponse(pending, response = {}) {
|
|
|
449
449
|
export function createOpenCodeEnvelopeTranslator({ sessionId, emit, onTurnEnd = () => {}, onUsageEvent = () => {}, brain = {}, planTurn = () => false } = {}) {
|
|
450
450
|
// costUsd is the session's running total: OpenCode reports cost per assistant
|
|
451
451
|
// message, so costs are summed by message id (the runtime takes deltas).
|
|
452
|
-
const state = { sessionId, roles: new Map(), order: [], textParts: new Map(), reasoningParts: new Map(), tools: new Map(), started: false, announcedInit: false, lastText: '', costUsd: 0, costByMessage: new Map(), usage: null };
|
|
452
|
+
const state = { sessionId, roles: new Map(), order: [], textParts: new Map(), reasoningParts: new Map(), partKinds: new Map(), tools: new Map(), started: false, announcedInit: false, lastText: '', costUsd: 0, costByMessage: new Map(), usage: null };
|
|
453
453
|
const event = (payload) => emit({ type: 'event', event: payload });
|
|
454
454
|
const belongs = (ev) => {
|
|
455
455
|
const sid = ev?.sessionID || ev?.sessionId || ev?.part?.sessionID || ev?.info?.sessionID || ev?.info?.id || ev?.session?.id || null;
|
|
@@ -485,12 +485,15 @@ export function createOpenCodeEnvelopeTranslator({ sessionId, emit, onTurnEnd =
|
|
|
485
485
|
function handlePart(part = {}) {
|
|
486
486
|
if (!part.messageID) return;
|
|
487
487
|
if (state.roles.get(part.messageID) === 'user') return;
|
|
488
|
+
if ((part.type === 'text' || part.type === 'reasoning') && part.id) state.partKinds.set(part.id, { messageID: part.messageID, type: part.type });
|
|
488
489
|
if (part.type === 'text' && typeof part.text === 'string') {
|
|
489
490
|
let parts = state.textParts.get(part.messageID);
|
|
490
491
|
if (!parts) { parts = new Map(); state.textParts.set(part.messageID, parts); }
|
|
491
492
|
const key = part.id || `${parts.size}`;
|
|
492
493
|
const prev = parts.get(key) || '';
|
|
493
494
|
const next = part.text;
|
|
495
|
+
// An update made before the deltas that already arrived streams nothing again.
|
|
496
|
+
if (prev.length > next.length && prev.startsWith(next)) return;
|
|
494
497
|
parts.set(key, next);
|
|
495
498
|
const delta = next.startsWith(prev) ? next.slice(prev.length) : next;
|
|
496
499
|
const shown = delta && planTurn() === true ? planTextDelta(`${part.messageID}:${key}`, delta) : delta;
|
|
@@ -500,6 +503,7 @@ export function createOpenCodeEnvelopeTranslator({ sessionId, emit, onTurnEnd =
|
|
|
500
503
|
if (part.type === 'reasoning' && typeof part.text === 'string') {
|
|
501
504
|
const key = `${part.messageID}:${part.id || 'r'}`;
|
|
502
505
|
const prev = state.reasoningParts.get(key) || '';
|
|
506
|
+
if (prev.length > part.text.length && prev.startsWith(part.text)) return;
|
|
503
507
|
state.reasoningParts.set(key, part.text);
|
|
504
508
|
const delta = part.text.startsWith(prev) ? part.text.slice(prev.length) : part.text;
|
|
505
509
|
if (delta) { startIfNeeded(); event(thinkingDelta(delta)); }
|
|
@@ -521,6 +525,19 @@ export function createOpenCodeEnvelopeTranslator({ sessionId, emit, onTurnEnd =
|
|
|
521
525
|
}
|
|
522
526
|
}
|
|
523
527
|
}
|
|
528
|
+
// A part's text arrives as `message.part.delta` between the update that announces the part and the one that
|
|
529
|
+
// ends it (OpenCode 2 streams only this way, through lib/opencode2-translate.js): each delta extends that part,
|
|
530
|
+
// and streams as an update would. A delta for a part not announced yet is left to the update that ends it,
|
|
531
|
+
// which carries the whole text.
|
|
532
|
+
function handleDelta(ev = {}) {
|
|
533
|
+
if ((ev.field && ev.field !== 'text') || typeof ev.delta !== 'string' || !ev.delta) return;
|
|
534
|
+
const known = state.partKinds.get(ev.partID);
|
|
535
|
+
if (!known || (ev.messageID && ev.messageID !== known.messageID)) return;
|
|
536
|
+
const prev = known.type === 'text'
|
|
537
|
+
? state.textParts.get(known.messageID)?.get(ev.partID) || ''
|
|
538
|
+
: state.reasoningParts.get(`${known.messageID}:${ev.partID}`) || '';
|
|
539
|
+
handlePart({ id: ev.partID, messageID: known.messageID, type: known.type, text: `${prev}${ev.delta}` });
|
|
540
|
+
}
|
|
524
541
|
return {
|
|
525
542
|
state,
|
|
526
543
|
handle(eventType, ev = {}) {
|
|
@@ -546,6 +563,7 @@ export function createOpenCodeEnvelopeTranslator({ sessionId, emit, onTurnEnd =
|
|
|
546
563
|
return true;
|
|
547
564
|
}
|
|
548
565
|
if (/^message[.:]part[.:]updated$/i.test(eventType)) { handlePart(ev.part || {}); return true; }
|
|
566
|
+
if (/^message[.:]part[.:]delta$/i.test(eventType)) { handleDelta(ev); return true; }
|
|
549
567
|
if (/^session[.:]idle$/i.test(eventType)) {
|
|
550
568
|
if (state.errored) { state.errored = false; state.started = false; return true; } // error already closed this turn
|
|
551
569
|
const said = latestAssistantText();
|
|
@@ -19,6 +19,7 @@ import { mkdirSync } from 'node:fs';
|
|
|
19
19
|
import { tmpdir } from 'node:os';
|
|
20
20
|
import { dirname, resolve, sep } from 'node:path';
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
|
+
import { CODEX_PROBE_SANDBOX_ARGS } from './agent-sandbox-elevation.js';
|
|
22
23
|
|
|
23
24
|
export const CODEX_GATE_HOOK_PATH = resolve(dirname(fileURLToPath(import.meta.url)), 'assistant-brains', 'codex-route-gate-hook.mjs');
|
|
24
25
|
export const CODEX_BROWSER_POLICY_HOOK_PATH = resolve(dirname(fileURLToPath(import.meta.url)), 'assistant-brains', 'codex-browser-policy-hook.mjs');
|
|
@@ -80,7 +81,8 @@ function probe({ codexBin, command, timeoutMs, spawnImpl }) {
|
|
|
80
81
|
if (codexNeedsShell(bin)) { done(null); return; }
|
|
81
82
|
const home = resolve(tmpdir(), 'synabun-codex-gate-probe');
|
|
82
83
|
try { mkdirSync(home, { recursive: true }); } catch {}
|
|
83
|
-
|
|
84
|
+
// A probe runs no command: the Windows sandbox that needs no administrator (lib/agent-sandbox-elevation.js).
|
|
85
|
+
const args = [...codexGateHookFlags(command), ...CODEX_PROBE_SANDBOX_ARGS, 'app-server'];
|
|
84
86
|
if (/\.js$/i.test(bin)) { args.unshift(bin); bin = process.execPath; }
|
|
85
87
|
let proc;
|
|
86
88
|
try {
|