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,508 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SynaBun — the supervisor's run of the server
|
|
3
|
+
*
|
|
4
|
+
* setup.js starts the Neural Interface server (neural-interface/server.js) as a
|
|
5
|
+
* child through this module, and decides every time that child ends what comes
|
|
6
|
+
* next (lib/server-exit.js serverExitDecision):
|
|
7
|
+
*
|
|
8
|
+
* restart it asked to be started again (exit code 75, or the restart marker)
|
|
9
|
+
* crashed it ended on its own after it was up: started again within the
|
|
10
|
+
* crash-loop guard (5 starts in 60 s), and the record of the crash
|
|
11
|
+
* (lib/server-crash-record.js) is left for the server it starts,
|
|
12
|
+
* which tells the owner's pages once that it happened
|
|
13
|
+
* failed it ended before it was listening: the start reports it and ends
|
|
14
|
+
* stopped it was asked to stop (the stop marker, or the stop line on its
|
|
15
|
+
* stdout with this start's token), or it ended cleanly: the
|
|
16
|
+
* supervisor ends too
|
|
17
|
+
*
|
|
18
|
+
* Asked both to restart and to stop, the later request wins: a stop asked while
|
|
19
|
+
* a restart shut down (the desktop app quitting) is a stop. A marker that is
|
|
20
|
+
* there but cannot be read (a scanner holding it) is read again a few times; a
|
|
21
|
+
* stop marker that stays unreadable is still read as the stop it stands for.
|
|
22
|
+
*
|
|
23
|
+
* The end is decided once the child's output has been read to the end
|
|
24
|
+
* (watchChildEnd), so a crash's last lines are kept, and its stdout is read in
|
|
25
|
+
* whole lines: a line split across two reads is still one line.
|
|
26
|
+
*
|
|
27
|
+
* When the supervisor ends because the server could not be kept up (the guard
|
|
28
|
+
* gave up, a start failed), it says so first (the supervisor record, the restart
|
|
29
|
+
* report) and then waits, a bounded time, for what Windows recorded about a
|
|
30
|
+
* native crash, which it writes to its logs as soon as each part is found.
|
|
31
|
+
*
|
|
32
|
+
* Everything outside this process is handed in (the record, the restart report,
|
|
33
|
+
* the browser, the Windows lookup, the exit), so
|
|
34
|
+
* tests/server-supervisor.test.mjs runs this with real child processes over a
|
|
35
|
+
* temporary data home. Never run setup.js to check it.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { spawn as nodeSpawn } from 'node:child_process';
|
|
39
|
+
import { randomBytes } from 'node:crypto';
|
|
40
|
+
import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync } from 'node:fs';
|
|
41
|
+
import { join, resolve } from 'node:path';
|
|
42
|
+
import {
|
|
43
|
+
MARKER_CLOCK_SLACK_MS, RESTART_EXIT_CODE, STOP_MARKER_NAME, STOP_TOKEN_ENV, createRestartGuard, describeExit, exitRecordFields, formatExit,
|
|
44
|
+
readRestartMarker, readStopAnnouncement, readStopMarker, serverExitDecision, watchChildEnd,
|
|
45
|
+
} from './server-exit.js';
|
|
46
|
+
import { clearCrashRecord, writeCrashRecord } from './server-crash-record.js';
|
|
47
|
+
import { describeWindowsCrash, lookupWindowsCrash as nodeLookupWindowsCrash } from './windows-crash-event.js';
|
|
48
|
+
|
|
49
|
+
/** The crash-loop guard: more than CRASH_LIMIT starts within CRASH_WINDOW_MS end the supervisor. */
|
|
50
|
+
export const CRASH_WINDOW_MS = 60_000;
|
|
51
|
+
export const CRASH_LIMIT = 5;
|
|
52
|
+
/** How long the server's output is read after it exited, at most, before its end is decided. */
|
|
53
|
+
export const SERVER_OUTPUT_DRAIN_MS = 1500;
|
|
54
|
+
/** The pause before a server is started again (its port is let go). */
|
|
55
|
+
export const RESPAWN_DELAY_MS = 250;
|
|
56
|
+
/**
|
|
57
|
+
* How long a supervisor that ends waits, at most, for what Windows recorded about
|
|
58
|
+
* a native crash (lib/windows-crash-event.js tries at 1.5, 7.5 and 22.5 s). The
|
|
59
|
+
* lookup is given this less CRASH_LOOKUP_MARGIN_MS as its whole budget, waits and
|
|
60
|
+
* queries together, so it ends, with what it found written, before the wait does.
|
|
61
|
+
*/
|
|
62
|
+
export const CRASH_LOOKUP_WAIT_MS = 30_000;
|
|
63
|
+
export const CRASH_LOOKUP_MARGIN_MS = 2000;
|
|
64
|
+
/** Each log file is moved aside past this size, when the supervisor starts. */
|
|
65
|
+
export const LOG_ROTATE_BYTES = 50 * 1024 * 1024;
|
|
66
|
+
/**
|
|
67
|
+
* A marker that is there but cannot be read (Windows: a scanner holds a file it just saw written) is read again,
|
|
68
|
+
* this many times in all, this far apart, before the end is decided without its words.
|
|
69
|
+
*/
|
|
70
|
+
export const MARKER_READ_ATTEMPTS = 6;
|
|
71
|
+
export const MARKER_RETRY_MS = 150;
|
|
72
|
+
// What a read of a file someone else holds for a moment answers.
|
|
73
|
+
const HELD = new Set(['EPERM', 'EBUSY', 'EACCES', 'EAGAIN', 'EMFILE', 'ENFILE']);
|
|
74
|
+
const MAX_LINE = 64 * 1024;
|
|
75
|
+
|
|
76
|
+
/** Whole lines out of a stream's chunks: `push(chunk)` hands each complete line to `onLine`, `end()` the rest. */
|
|
77
|
+
export function createLineReader(onLine, { maxLine = MAX_LINE } = {}) {
|
|
78
|
+
let rest = '';
|
|
79
|
+
const emit = (line) => { try { onLine(line.endsWith('\r') ? line.slice(0, -1) : line); } catch {} };
|
|
80
|
+
return {
|
|
81
|
+
push(chunk) {
|
|
82
|
+
rest += String(chunk);
|
|
83
|
+
let at;
|
|
84
|
+
while ((at = rest.indexOf('\n')) >= 0) {
|
|
85
|
+
emit(rest.slice(0, at));
|
|
86
|
+
rest = rest.slice(at + 1);
|
|
87
|
+
}
|
|
88
|
+
// A line that never ends is read in pieces, never kept whole.
|
|
89
|
+
while (rest.length > maxLine) {
|
|
90
|
+
emit(rest.slice(0, maxLine));
|
|
91
|
+
rest = rest.slice(maxLine);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
end() {
|
|
95
|
+
if (rest) emit(rest);
|
|
96
|
+
rest = '';
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** The address the server says it listens on (" Server: http://localhost:3344"): { url, port } or null. */
|
|
102
|
+
export function listeningAddress(line) {
|
|
103
|
+
const found = /^\s*Server:\s+(https?:\/\/\S+)/.exec(String(line ?? ''));
|
|
104
|
+
if (!found) return null;
|
|
105
|
+
let port = null;
|
|
106
|
+
try {
|
|
107
|
+
const url = new URL(found[1]);
|
|
108
|
+
port = Number(url.port || (url.protocol === 'https:' ? 443 : 80));
|
|
109
|
+
} catch {}
|
|
110
|
+
return { url: found[1], port: Number.isInteger(port) && port > 0 && port < 65536 ? port : null };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function within(promise, ms) {
|
|
114
|
+
return new Promise((done) => {
|
|
115
|
+
const timer = setTimeout(done, ms);
|
|
116
|
+
Promise.resolve(promise).catch(() => {}).then(() => { clearTimeout(timer); done(); });
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Run the server and keep it up. Returns { stop(signal), child }: `stop` ends
|
|
122
|
+
* it on the supervisor's own signal (SIGINT, SIGTERM) and exits.
|
|
123
|
+
*
|
|
124
|
+
* @param {object} o
|
|
125
|
+
* @param {string} o.serverPath neural-interface/server.js
|
|
126
|
+
* @param {string} o.cwd the folder it runs in
|
|
127
|
+
* @param {string} o.dataHome SynaBun's data home (markers, records, logs)
|
|
128
|
+
* @param {() => object} [o.env] the environment the server gets, read at each start
|
|
129
|
+
* @param {{ info: Function, warn: Function, fail: Function }} [o.print] the supervisor's own lines
|
|
130
|
+
* @param {(name: string, detail?: object) => void} [o.onPhase] the supervisor record's phase
|
|
131
|
+
* @param {(code: string, message: string, extra?: object) => void} [o.onFailed] the record's failure
|
|
132
|
+
* @param {(o: { port: number|null, first: boolean }) => void} [o.onListening] the server said it listens
|
|
133
|
+
* @param {{ reopen: (port: number) => void, failed: (error: object) => Promise<void> }} [o.reports] the restart report
|
|
134
|
+
* @param {(code: number) => void} [o.exit]
|
|
135
|
+
*/
|
|
136
|
+
export function superviseServer({
|
|
137
|
+
serverPath,
|
|
138
|
+
cwd,
|
|
139
|
+
dataHome,
|
|
140
|
+
execPath = process.execPath,
|
|
141
|
+
nodeArgs = ['--disable-warning=ExperimentalWarning', '--max-old-space-size=6144'],
|
|
142
|
+
env = () => process.env,
|
|
143
|
+
windowsHide = false,
|
|
144
|
+
platform = process.platform,
|
|
145
|
+
spawn = nodeSpawn,
|
|
146
|
+
logDir = join(dataHome, 'data'),
|
|
147
|
+
print = { info() {}, warn() {}, fail() {} },
|
|
148
|
+
passthrough = { stdout: process.stdout, stderr: process.stderr },
|
|
149
|
+
onPhase = () => {},
|
|
150
|
+
onFailed = () => {},
|
|
151
|
+
onListening = () => {},
|
|
152
|
+
reports = { reopen() {}, async failed() {} },
|
|
153
|
+
resolvePort = () => null,
|
|
154
|
+
lookupWindowsCrash = nodeLookupWindowsCrash,
|
|
155
|
+
exit = (code) => process.exit(code),
|
|
156
|
+
windowMs = CRASH_WINDOW_MS,
|
|
157
|
+
limit = CRASH_LIMIT,
|
|
158
|
+
drainMs = SERVER_OUTPUT_DRAIN_MS,
|
|
159
|
+
respawnDelayMs = RESPAWN_DELAY_MS,
|
|
160
|
+
crashLookupWaitMs = CRASH_LOOKUP_WAIT_MS,
|
|
161
|
+
markerReadAttempts = MARKER_READ_ATTEMPTS,
|
|
162
|
+
markerRetryMs = MARKER_RETRY_MS,
|
|
163
|
+
files = { readFileSync, statSync },
|
|
164
|
+
sleep = (ms) => new Promise((done) => setTimeout(done, ms)),
|
|
165
|
+
newToken = () => randomBytes(16).toString('hex'),
|
|
166
|
+
now = () => Date.now(),
|
|
167
|
+
} = {}) {
|
|
168
|
+
if (!serverPath || !dataHome) throw new Error('superviseServer needs the server and the data home');
|
|
169
|
+
|
|
170
|
+
// Marker files the server writes before it ends: a restart it asked for (server.js /api/server/restart; a
|
|
171
|
+
// native teardown crash can clobber its exit code 75), and a stop someone asked for (its exit status cannot say
|
|
172
|
+
// so: server.js ends itself by force after its cleanup, which Windows reports as exit code 1 and macOS / Linux
|
|
173
|
+
// as SIGKILL). A marker from a run before this one says nothing about this one.
|
|
174
|
+
const restartMarker = resolve(dataHome, 'restart-requested');
|
|
175
|
+
const stopMarker = resolve(dataHome, STOP_MARKER_NAME);
|
|
176
|
+
const remove = (path) => { try { if (existsSync(path)) unlinkSync(path); } catch {} };
|
|
177
|
+
remove(restartMarker);
|
|
178
|
+
remove(stopMarker);
|
|
179
|
+
// What a stop marker left half-written when its server died between the write and the rename.
|
|
180
|
+
try {
|
|
181
|
+
for (const name of readdirSync(dataHome)) if (/^stop-requested\.\d+\.tmp$/.test(name)) remove(join(dataHome, name));
|
|
182
|
+
} catch {}
|
|
183
|
+
|
|
184
|
+
// The server's stdout and stderr go to log files too, whoever launched the supervisor (the Apps menu, a
|
|
185
|
+
// terminal, launchd, the desktop app): the files a person sends when they report a crash.
|
|
186
|
+
try { mkdirSync(logDir, { recursive: true }); } catch {}
|
|
187
|
+
const stdoutPath = resolve(logDir, 'server-stdout.log');
|
|
188
|
+
const stderrPath = resolve(logDir, 'server-stderr.log');
|
|
189
|
+
for (const path of [stdoutPath, stderrPath]) {
|
|
190
|
+
try { if (existsSync(path) && statSync(path).size > LOG_ROTATE_BYTES) renameSync(path, `${path}.1`); } catch {}
|
|
191
|
+
}
|
|
192
|
+
let outLog = null;
|
|
193
|
+
let errLog = null;
|
|
194
|
+
// A full disk reports asynchronously from WriteStream.write(): without an error listener that event would end
|
|
195
|
+
// the supervisor itself. Only the stream that failed is given up; supervision goes on.
|
|
196
|
+
const openLog = (kind, path) => {
|
|
197
|
+
let stream = null;
|
|
198
|
+
try { stream = createWriteStream(path, { flags: 'a' }); } catch (error) {
|
|
199
|
+
print.warn(`Could not open ${kind} log: ${error.message}`);
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
stream.on('error', (error) => {
|
|
203
|
+
if (kind === 'stdout' && outLog === stream) outLog = null;
|
|
204
|
+
if (kind === 'stderr' && errLog === stream) errLog = null;
|
|
205
|
+
print.warn(`Could not persist ${kind} server log: ${error.message} — continuing without file capture.`);
|
|
206
|
+
try { stream.destroy(); } catch {}
|
|
207
|
+
});
|
|
208
|
+
return stream;
|
|
209
|
+
};
|
|
210
|
+
outLog = openLog('stdout', stdoutPath);
|
|
211
|
+
errLog = openLog('stderr', stderrPath);
|
|
212
|
+
const stamp = `\n===== supervisor start ${new Date(now()).toISOString()} (pid ${process.pid}) =====\n`;
|
|
213
|
+
try { outLog?.write(stamp); errLog?.write(stamp); } catch {}
|
|
214
|
+
|
|
215
|
+
const guard = createRestartGuard({ windowMs, limit, now });
|
|
216
|
+
const seconds = Math.round(windowMs / 1000);
|
|
217
|
+
const state = { child: null, firstLaunch: true, shuttingDown: false, ending: false, listenPort: null };
|
|
218
|
+
|
|
219
|
+
// What the supervisor says about the server's end also goes into the server's own error log, beside the
|
|
220
|
+
// server's last lines. Once the logs are closed a late line is dropped, never written to an ended stream.
|
|
221
|
+
const noteInServerLog = (line) => {
|
|
222
|
+
try { errLog?.write(`[supervisor ${new Date(now()).toISOString()}] ${line}\n`); } catch {}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Windows: what Windows Error Reporting recorded about a native crash (the module, the fail-fast code, the
|
|
226
|
+
// report's folder), read from the Application log, each part logged as soon as it is found. The next server is
|
|
227
|
+
// never held up for it; a supervisor that ends waits for it, a bounded time (settle).
|
|
228
|
+
const lookupCrash = (pid, since, status) => {
|
|
229
|
+
if (platform !== 'win32' || status.kind !== 'native-crash') return null;
|
|
230
|
+
const said = new Set();
|
|
231
|
+
const tell = (found) => {
|
|
232
|
+
const line = found?.crash ? describeWindowsCrash(found.crash, found.report) : '';
|
|
233
|
+
if (!line || said.has(line)) return;
|
|
234
|
+
said.add(line);
|
|
235
|
+
print.info(line);
|
|
236
|
+
noteInServerLog(line);
|
|
237
|
+
};
|
|
238
|
+
// Its budget fits in the wait of a supervisor that ends: nothing it finds is cut off by that wait.
|
|
239
|
+
const budgetMs = Math.max(0, crashLookupWaitMs - CRASH_LOOKUP_MARGIN_MS);
|
|
240
|
+
return Promise.resolve()
|
|
241
|
+
.then(() => lookupWindowsCrash({ pid, since, platform, onFound: tell, budgetMs }))
|
|
242
|
+
.then((found) => {
|
|
243
|
+
if (found) tell(found);
|
|
244
|
+
else {
|
|
245
|
+
const line = `No Windows Error Reporting event was found for the crash of pid ${pid}.`;
|
|
246
|
+
print.info(line);
|
|
247
|
+
noteInServerLog(line);
|
|
248
|
+
}
|
|
249
|
+
return found;
|
|
250
|
+
}, () => null);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// What is still on its way to the log files is written before the supervisor goes: a crash's last lines are
|
|
254
|
+
// the ones that say why. So is what Windows recorded about it, `crashLookupWaitMs` at the most.
|
|
255
|
+
const closeStream = (stream) => new Promise((done) => {
|
|
256
|
+
if (!stream) { done(); return; }
|
|
257
|
+
try { stream.end(done); } catch { done(); }
|
|
258
|
+
});
|
|
259
|
+
async function settle(pending = null) {
|
|
260
|
+
if (pending) await within(pending, crashLookupWaitMs);
|
|
261
|
+
const streams = [outLog, errLog];
|
|
262
|
+
outLog = null;
|
|
263
|
+
errLog = null;
|
|
264
|
+
await within(Promise.allSettled(streams.map(closeStream)), 1500);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// The supervisor ends without a server: it has said so already (onFailed); the restart report says it to a
|
|
268
|
+
// page that waits (lib/start-launcher.js openRestartReport), while the logs get their last lines.
|
|
269
|
+
function endWithoutServer(code, error, pending = null) {
|
|
270
|
+
if (state.ending) return;
|
|
271
|
+
state.ending = true;
|
|
272
|
+
// The crash it recovered from is not news any more: no server came back up to tell it.
|
|
273
|
+
clearCrashRecord(dataHome);
|
|
274
|
+
Promise.allSettled([
|
|
275
|
+
settle(pending),
|
|
276
|
+
Promise.resolve().then(() => reports.failed(error)),
|
|
277
|
+
]).finally(() => exit(code));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// A marker file as it is now: { state: 'none' }, { state: 'read', text, mtimeMs } or, when it is there and could
|
|
281
|
+
// not be read after a few tries (a scanner holding it), { state: 'unreadable', code, mtimeMs }.
|
|
282
|
+
const mtimeOf = (path) => {
|
|
283
|
+
try { const ms = files.statSync(path).mtimeMs; return Number.isFinite(ms) ? ms : null; } catch { return null; }
|
|
284
|
+
};
|
|
285
|
+
async function readMarkerFile(path) {
|
|
286
|
+
let failure = null;
|
|
287
|
+
for (let attempt = 0; attempt < Math.max(1, markerReadAttempts); attempt++) {
|
|
288
|
+
if (attempt) await sleep(markerRetryMs);
|
|
289
|
+
try {
|
|
290
|
+
return { state: 'read', text: String(files.readFileSync(path, 'utf8')), mtimeMs: mtimeOf(path) };
|
|
291
|
+
} catch (error) {
|
|
292
|
+
if (error?.code === 'ENOENT') return { state: 'none' };
|
|
293
|
+
failure = error;
|
|
294
|
+
if (!HELD.has(error?.code)) break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return { state: 'unreadable', code: String(failure?.code || failure?.message || 'unknown').slice(0, 40), mtimeMs: mtimeOf(path) };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// The restart marker: { marked, at } (at: when the restart was asked for, when that can be told).
|
|
301
|
+
async function takeRestartMarker() {
|
|
302
|
+
const file = await readMarkerFile(restartMarker);
|
|
303
|
+
if (file.state === 'none') return { marked: false, at: null };
|
|
304
|
+
remove(restartMarker);
|
|
305
|
+
return { marked: true, at: (file.state === 'read' ? readRestartMarker(file.text) : null) ?? file.mtimeMs };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// The stop marker, taken: { reason, at } of a stop this server asked for, or null. One that is there (its file is
|
|
309
|
+
// seen) but stays unreadable is only ever written for a stop someone asked for, so it is read as a stop (and said
|
|
310
|
+
// so), unless it was plainly written before this server started. One that cannot even be seen (Windows: a file
|
|
311
|
+
// being deleted while something still holds it) proves nothing.
|
|
312
|
+
async function takeStopMarker(pid, since) {
|
|
313
|
+
const file = await readMarkerFile(stopMarker);
|
|
314
|
+
if (file.state === 'none') return null;
|
|
315
|
+
remove(stopMarker);
|
|
316
|
+
if (file.state === 'read') return readStopMarker(file.text, { pid, since });
|
|
317
|
+
if (file.mtimeMs === null) {
|
|
318
|
+
noteInServerLog(`A stop marker could not be read or seen (${file.code}) when the server (pid ${pid}) ended: it says nothing about this end.`);
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
if (file.mtimeMs < since - MARKER_CLOCK_SLACK_MS) return null;
|
|
322
|
+
const line = `The stop marker of the server (pid ${pid}) is there but could not be read (${file.code}): its end is read as the stop it stands for.`;
|
|
323
|
+
print.warn(line);
|
|
324
|
+
noteInServerLog(line);
|
|
325
|
+
return { reason: '', at: file.mtimeMs, unreadable: true };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// The server has ended and its output was read: what it left (its markers) is read, then it is started again, or
|
|
329
|
+
// the supervisor ends too.
|
|
330
|
+
function onServerEnd(child, status, info) {
|
|
331
|
+
if (state.child === child) state.child = null;
|
|
332
|
+
if (state.shuttingDown || state.ending) return;
|
|
333
|
+
// (decided once: a read that failed decides without the markers, a decision that failed is only said)
|
|
334
|
+
Promise.all([takeRestartMarker(), takeStopMarker(child.pid, info.spawnedAt)]).then(
|
|
335
|
+
([restart, marker]) => decideEnd(child, status, { ...info, restart, marker }),
|
|
336
|
+
(error) => {
|
|
337
|
+
print.fail(`The markers the server left could not be read: ${error?.message || error}`);
|
|
338
|
+
decideEnd(child, status, { ...info, restart: { marked: false, at: null }, marker: null });
|
|
339
|
+
},
|
|
340
|
+
).catch((error) => print.fail(`The end of the server could not be decided: ${error?.message || error}`));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function decideEnd(child, { code, signal }, { listened, spawnedAt, announced, restart, marker }) {
|
|
344
|
+
if (state.shuttingDown || state.ending) return;
|
|
345
|
+
const restartMarked = restart.marked;
|
|
346
|
+
const stop = marker || announced;
|
|
347
|
+
// Both asked for: the later request wins (a stop asked while a restart shut down: the desktop app quitting).
|
|
348
|
+
const stopAfterRestart = !!stop && restartMarked && Number.isFinite(stop.at) && Number.isFinite(restart.at) && stop.at >= restart.at;
|
|
349
|
+
const status = describeExit({ code, signal });
|
|
350
|
+
const decision = serverExitDecision({ code, signal, restartRequested: restartMarked, stopRequested: !!stop, stopAfterRestart, listened });
|
|
351
|
+
|
|
352
|
+
if (decision === 'restart' || decision === 'crashed') {
|
|
353
|
+
const crashed = decision === 'crashed';
|
|
354
|
+
if (crashed) {
|
|
355
|
+
// Nobody asked for this end: the server crashed after it was up. It is started again, as a restart is,
|
|
356
|
+
// within the same crash-loop guard.
|
|
357
|
+
print.fail(`The server crashed: ${formatExit(status)}.`);
|
|
358
|
+
noteInServerLog(`The server (pid ${child.pid}) crashed: ${formatExit(status)}. Starting it again.`);
|
|
359
|
+
} else if (code === RESTART_EXIT_CODE) {
|
|
360
|
+
print.info('Server requested restart — respawning...');
|
|
361
|
+
} else {
|
|
362
|
+
print.info(`Server exited with code ${code} but restart marker present — respawning.`);
|
|
363
|
+
}
|
|
364
|
+
const lookup = crashed ? lookupCrash(child.pid, spawnedAt, status) : null;
|
|
365
|
+
const { count, exceeded } = guard.note();
|
|
366
|
+
// The page that asked for this has no server to ask now: from here until the new one is listening, this
|
|
367
|
+
// supervisor says what happens.
|
|
368
|
+
try { reports.reopen(state.listenPort || resolvePort()); } catch {}
|
|
369
|
+
if (exceeded) {
|
|
370
|
+
// A loop of crashes says how the last one ended (the app's stopped page names it); restarts asked for do not.
|
|
371
|
+
const message = crashed
|
|
372
|
+
? `the server stopped ${count} times in ${seconds} s (the last time: ${status.text})`
|
|
373
|
+
: `the server restarted ${count} times in ${seconds} s`;
|
|
374
|
+
const fields = crashed ? exitRecordFields(status) : {};
|
|
375
|
+
print.fail(`Server restarted ${count} times in ${seconds}s — aborting supervisor.`);
|
|
376
|
+
onFailed('RESTART_LOOP', message, fields);
|
|
377
|
+
endWithoutServer(1, { code: 'RESTART_LOOP', message, ...fields }, lookup);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (crashed) {
|
|
381
|
+
// The server it starts tells the owner's pages, once, what happened and what was lost.
|
|
382
|
+
if (!writeCrashRecord(dataHome, { exit: status, at: now(), uptimeMs: Math.max(0, now() - spawnedAt) })) {
|
|
383
|
+
print.warn('Could not leave the crash notice for the next server.');
|
|
384
|
+
}
|
|
385
|
+
print.info(`Starting it again (${count} of ${limit} restarts within ${seconds} s).`);
|
|
386
|
+
}
|
|
387
|
+
state.firstLaunch = false;
|
|
388
|
+
setTimeout(spawnOnce, respawnDelayMs);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (decision === 'failed') {
|
|
393
|
+
// It ended before it was listening: a start that failed. Starting it again would fail the same way, so the
|
|
394
|
+
// start reports it and ends.
|
|
395
|
+
const message = `the server exited before it was listening (${status.text})`;
|
|
396
|
+
print.fail(`Server exited with code ${code ?? signal}: ${formatExit(status)}`);
|
|
397
|
+
noteInServerLog(`The server (pid ${child.pid}) ended before it was listening: ${formatExit(status)}.`);
|
|
398
|
+
onFailed('SERVER_EXITED', message, exitRecordFields(status));
|
|
399
|
+
endWithoutServer(Number.isInteger(code) && code !== 0 ? code : 1, { code: 'SERVER_EXITED', message, ...exitRecordFields(status) }, lookupCrash(child.pid, spawnedAt, status));
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Stopped: asked for (Stop Server, the app quitting, an update), or a clean end.
|
|
404
|
+
state.ending = true;
|
|
405
|
+
print.info(`Server stopped${stop?.reason ? ` (${stop.reason})` : ''}.`);
|
|
406
|
+
if (stopAfterRestart) print.info('The restart it had asked for was called off by the stop asked after it.');
|
|
407
|
+
// A native crash on the way out (a native teardown abort) is still worth a line.
|
|
408
|
+
if (status.kind === 'native-crash') {
|
|
409
|
+
print.info(`It ended with ${formatExit(status)} while it stopped.`);
|
|
410
|
+
noteInServerLog(`The server (pid ${child.pid}) ended with ${formatExit(status)} while it stopped.`);
|
|
411
|
+
}
|
|
412
|
+
settle().finally(() => exit(0));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function spawnFailed(error) {
|
|
416
|
+
const message = `the server could not be started: ${error?.message || error}`;
|
|
417
|
+
print.fail(`Could not start the server: ${error?.message || error}`);
|
|
418
|
+
onFailed('SERVER_SPAWN_FAILED', message);
|
|
419
|
+
endWithoutServer(1, { code: 'SERVER_SPAWN_FAILED', message });
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function spawnOnce() {
|
|
423
|
+
if (state.shuttingDown || state.ending) return;
|
|
424
|
+
remove(stopMarker);
|
|
425
|
+
const spawnedAt = now();
|
|
426
|
+
// This start's token: the server prints it with a stop someone asked of it (lib/server-exit.js
|
|
427
|
+
// stopAnnouncement), which tells the stop without the data folder.
|
|
428
|
+
const token = newToken();
|
|
429
|
+
let child;
|
|
430
|
+
try {
|
|
431
|
+
child = spawn(execPath, [...nodeArgs, serverPath], {
|
|
432
|
+
cwd,
|
|
433
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
434
|
+
windowsHide,
|
|
435
|
+
env: {
|
|
436
|
+
...(typeof env === 'function' ? env() : env),
|
|
437
|
+
SYNABUN_DATA_HOME: dataHome,
|
|
438
|
+
MEMORY_DATA_DIR: resolve(dataHome, 'mcp-data'),
|
|
439
|
+
SYNABUN_SUPERVISED: '1',
|
|
440
|
+
[STOP_TOKEN_ENV]: token,
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
} catch (error) {
|
|
444
|
+
spawnFailed(error);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
state.child = child;
|
|
448
|
+
onPhase('server', { pid: child.pid });
|
|
449
|
+
|
|
450
|
+
const first = state.firstLaunch;
|
|
451
|
+
let listened = false;
|
|
452
|
+
let announced = null;
|
|
453
|
+
const lines = createLineReader((line) => {
|
|
454
|
+
const said = readStopAnnouncement(line, token);
|
|
455
|
+
// When it was said, by this supervisor's clock (the latest, when it is said again): a restart asked for
|
|
456
|
+
// before it gives way to it.
|
|
457
|
+
if (said) { announced = { ...said, at: now() }; return; }
|
|
458
|
+
if (listened) return;
|
|
459
|
+
// The server prints its address from its listen callback: it is up.
|
|
460
|
+
const address = listeningAddress(line);
|
|
461
|
+
if (!address) return;
|
|
462
|
+
listened = true;
|
|
463
|
+
if (address.port) state.listenPort = address.port;
|
|
464
|
+
onPhase('listening', { port: address.port || undefined });
|
|
465
|
+
try { onListening({ port: address.port, first }); } catch {}
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// Its end is decided once its output has been read to the end, or a moment after it exited when something it
|
|
469
|
+
// started still holds that output open: a crash's last words are its last lines.
|
|
470
|
+
const end = watchChildEnd(child, {
|
|
471
|
+
drainMs,
|
|
472
|
+
onEnd: (status) => {
|
|
473
|
+
lines.end();
|
|
474
|
+
onServerEnd(child, status, { listened, spawnedAt, announced });
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
child.on('error', (error) => {
|
|
478
|
+
// A server that could not be started at all is reported here, and not a second time as an end.
|
|
479
|
+
if (end.decided) return;
|
|
480
|
+
end.cancel();
|
|
481
|
+
if (state.child === child) state.child = null;
|
|
482
|
+
spawnFailed(error);
|
|
483
|
+
});
|
|
484
|
+
child.stdout?.setEncoding?.('utf8');
|
|
485
|
+
child.stderr?.setEncoding?.('utf8');
|
|
486
|
+
child.stdout?.on('data', (text) => {
|
|
487
|
+
try { passthrough?.stdout?.write(text); } catch {}
|
|
488
|
+
try { outLog?.write(text); } catch {}
|
|
489
|
+
lines.push(text);
|
|
490
|
+
});
|
|
491
|
+
child.stderr?.on('data', (text) => {
|
|
492
|
+
try { passthrough?.stderr?.write(text); } catch {}
|
|
493
|
+
try { errLog?.write(text); } catch {}
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
spawnOnce();
|
|
498
|
+
|
|
499
|
+
return {
|
|
500
|
+
get child() { return state.child; },
|
|
501
|
+
/** The supervisor's own signal: the server gets it too (its handler stops it gracefully), and this ends. */
|
|
502
|
+
stop(signal) {
|
|
503
|
+
state.shuttingDown = true;
|
|
504
|
+
try { state.child?.kill(signal); } catch {}
|
|
505
|
+
exit(0);
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
}
|
package/lib/start-launcher.js
CHANGED
|
@@ -549,10 +549,17 @@ export const SUPERVISOR_PHASES = Object.freeze([
|
|
|
549
549
|
|
|
550
550
|
function supervisorError(error) {
|
|
551
551
|
const exitCode = Number(error?.exitCode);
|
|
552
|
+
// How the server's exit reads (lib/server-exit.js exitRecordFields): the desktop app's stopped page names it.
|
|
553
|
+
const exitHex = typeof error?.exitHex === 'string' && /^0x[0-9A-F]{8}$/i.test(error.exitHex) ? error.exitHex : null;
|
|
554
|
+
const exitSignal = typeof error?.exitSignal === 'string' && /^SIG[A-Z0-9]{1,10}$/.test(error.exitSignal) ? error.exitSignal : null;
|
|
555
|
+
const exitKind = typeof error?.exitKind === 'string' && /^[a-z][a-z-]{0,23}$/.test(error.exitKind) ? error.exitKind : null;
|
|
552
556
|
return {
|
|
553
557
|
code: String(error?.code || 'SETUP_FAILED').slice(0, 40),
|
|
554
558
|
message: String(error?.message || error || 'the start failed').slice(0, 300),
|
|
555
559
|
...(Number.isInteger(exitCode) ? { exitCode } : {}),
|
|
560
|
+
...(exitHex ? { exitHex } : {}),
|
|
561
|
+
...(exitSignal ? { exitSignal } : {}),
|
|
562
|
+
...(exitKind ? { exitKind } : {}),
|
|
556
563
|
};
|
|
557
564
|
}
|
|
558
565
|
|
|
@@ -852,6 +859,8 @@ export function createStartBeacon({
|
|
|
852
859
|
let retryTimer = null;
|
|
853
860
|
let finalReads = 0;
|
|
854
861
|
let onFinalRead = null;
|
|
862
|
+
// Reads by a page (a request with an Origin), whatever the state: whether anybody follows this launch at all.
|
|
863
|
+
let pageReads = 0;
|
|
855
864
|
// Each phase the supervisor was seen in, with the time it gave for it. One
|
|
856
865
|
// that came and went between two looks is not here: this is what was seen.
|
|
857
866
|
const phases = [];
|
|
@@ -899,6 +908,7 @@ export function createStartBeacon({
|
|
|
899
908
|
'X-Content-Type-Options': 'nosniff',
|
|
900
909
|
});
|
|
901
910
|
res.end(body);
|
|
911
|
+
if (origin) pageReads++;
|
|
902
912
|
if (status.state !== 'starting' && origin) {
|
|
903
913
|
finalReads++;
|
|
904
914
|
onFinalRead?.();
|
|
@@ -976,17 +986,31 @@ export function createStartBeacon({
|
|
|
976
986
|
/**
|
|
977
987
|
* Keep answering until a page has read how it ended (and a moment more,
|
|
978
988
|
* for a second tab), `ms` at the most. A beacon nobody can reach is over at once.
|
|
989
|
+
* With `idleMs`, a beacon no page has read at all is over after `idleMs`: a
|
|
990
|
+
* page that follows a launch reads it every few hundred milliseconds, and
|
|
991
|
+
* one that has not looked by then is not waiting for it.
|
|
979
992
|
*/
|
|
980
|
-
linger(ms, { afterReadMs = 1500 } = {}) {
|
|
993
|
+
linger(ms, { afterReadMs = 1500, idleMs = 0 } = {}) {
|
|
981
994
|
if (!listening || closed || !(ms > 0)) return Promise.resolve();
|
|
982
995
|
return new Promise((done) => {
|
|
983
996
|
let timer = setTimeout(done, ms);
|
|
997
|
+
let idle = null;
|
|
984
998
|
const soon = () => {
|
|
985
999
|
onFinalRead = null;
|
|
986
1000
|
clearTimeout(timer);
|
|
1001
|
+
if (idle) clearTimeout(idle);
|
|
987
1002
|
timer = setTimeout(done, afterReadMs);
|
|
988
1003
|
};
|
|
989
|
-
if (finalReads > 0) soon();
|
|
1004
|
+
if (finalReads > 0) { soon(); return; }
|
|
1005
|
+
onFinalRead = soon;
|
|
1006
|
+
if (idleMs > 0 && pageReads === 0) {
|
|
1007
|
+
idle = setTimeout(() => {
|
|
1008
|
+
if (pageReads > 0) return;
|
|
1009
|
+
onFinalRead = null;
|
|
1010
|
+
clearTimeout(timer);
|
|
1011
|
+
done();
|
|
1012
|
+
}, Math.min(idleMs, ms));
|
|
1013
|
+
}
|
|
990
1014
|
});
|
|
991
1015
|
},
|
|
992
1016
|
close() {
|
|
@@ -1010,6 +1034,13 @@ export function createStartBeacon({
|
|
|
1010
1034
|
export const RESTART_REPORT_ENV = 'SYNABUN_RESTART_REPORT';
|
|
1011
1035
|
/** How long a restart's report stays once the server said it is listening: a page polls every 300 ms. */
|
|
1012
1036
|
export const RESTART_REPORT_SETTLE_MS = 3000;
|
|
1037
|
+
/**
|
|
1038
|
+
* How long a restart that ended without a server keeps saying so when no page
|
|
1039
|
+
* has read its report at all: a page that follows it reads it every 300 ms, an
|
|
1040
|
+
* idle one every 3 s just after it loads. After a crash nobody asked for, nobody
|
|
1041
|
+
* usually is (the app's stopped page reads the supervisor record instead).
|
|
1042
|
+
*/
|
|
1043
|
+
export const RESTART_REPORT_IDLE_MS = 4000;
|
|
1013
1044
|
|
|
1014
1045
|
/**
|
|
1015
1046
|
* A restart reports itself the way a launch does, on the start beacon, for as
|
|
@@ -1020,7 +1051,8 @@ export const RESTART_REPORT_SETTLE_MS = 3000;
|
|
|
1020
1051
|
*
|
|
1021
1052
|
* phase() — the supervisor's record changed: pass on what it says now
|
|
1022
1053
|
* failed(error) — it ended without a server; resolves once a page has read
|
|
1023
|
-
* why (`lingerMs` at the most
|
|
1054
|
+
* why (`lingerMs` at the most, `idleMs` when no page has
|
|
1055
|
+
* read the report at all), so the caller can exit then
|
|
1024
1056
|
* close() — over
|
|
1025
1057
|
*
|
|
1026
1058
|
* Only what the supervisor writes after the report began goes out: a record
|
|
@@ -1035,6 +1067,7 @@ export function openRestartReport({
|
|
|
1035
1067
|
now = () => Date.now(),
|
|
1036
1068
|
settleMs = RESTART_REPORT_SETTLE_MS,
|
|
1037
1069
|
lingerMs = BEACON_LINGER_MS,
|
|
1070
|
+
idleMs = RESTART_REPORT_IDLE_MS,
|
|
1038
1071
|
beacon = createStartBeacon,
|
|
1039
1072
|
} = {}) {
|
|
1040
1073
|
const startedAt = now();
|
|
@@ -1074,7 +1107,7 @@ export function openRestartReport({
|
|
|
1074
1107
|
settle = null;
|
|
1075
1108
|
report.finish('failed', error);
|
|
1076
1109
|
await listening; // a failure right after it opened: let it be reachable first
|
|
1077
|
-
try { await report.linger(lingerMs); } catch {}
|
|
1110
|
+
try { await report.linger(lingerMs, { idleMs }); } catch {}
|
|
1078
1111
|
close();
|
|
1079
1112
|
},
|
|
1080
1113
|
close,
|
package/lib/tools-cli.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { externalCatalog, selectExternalServers } from '../neural-interface/lib/mcp-external-servers.js';
|
|
2
|
+
// Loaded only by `synabun tools`; setup can still run before MCP dist exists.
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { toolCatalogView } from '../mcp-server/dist/services/tool-catalog.js';
|
|
6
|
+
import { parseToolList, resolveToolList } from '../mcp-server/dist/services/tool-list.js';
|
|
7
|
+
import { readDefaultTools, requireToolList, writeDefaultTools } from '../neural-interface/lib/tool-lists.js';
|
|
8
|
+
|
|
9
|
+
export function runToolsCommand(args = [], { dataHome, externalContext = {}, log = console.log, error = console.error } = {}) {
|
|
10
|
+
const [action = 'list', ...values] = args;
|
|
11
|
+
try {
|
|
12
|
+
if (action === 'list' && !values.length) {
|
|
13
|
+
const catalog = toolCatalogView();
|
|
14
|
+
log(`Always on: ${catalog.alwaysOnTools.join(', ')} (${catalog.alwaysOn})`);
|
|
15
|
+
for (const group of catalog.groups) log(`${group.id.padEnd(20)} ${String(group.count).padStart(2)} ${group.purpose}`);
|
|
16
|
+
log(`Host effect: ${Object.entries(catalog.effect).map(([host, effect]) => `${host} ${effect}`).join('; ')}.`);
|
|
17
|
+
log('Usage: synabun tools [list | get | set <ids>]. Use comma-separated ids, all, or [] for always-on only.');
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
if (action === 'get' && !values.length) {
|
|
21
|
+
let customPresets = null;
|
|
22
|
+
try { customPresets = JSON.parse(readFileSync(resolve(dataHome, 'data/mcp-registry.json'), 'utf8')).profiles; } catch {}
|
|
23
|
+
const tools = readDefaultTools(dataHome, { customPresets });
|
|
24
|
+
const selected = requireToolList(resolveToolList(tools, { source: 'runtime' }));
|
|
25
|
+
log(JSON.stringify({ tools, groups: selected.groups, count: selected.count, effect: toolCatalogView().effect }));
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
if (action === 'set' && values.length) {
|
|
29
|
+
const raw = values.join(',');
|
|
30
|
+
const items = requireToolList(parseToolList(raw === '[]' ? [] : raw)).items;
|
|
31
|
+
requireToolList(resolveToolList(items, { source: 'runtime' }));
|
|
32
|
+
if (items.some(item => item.startsWith('mcp:') && item !== 'mcp:*')) {
|
|
33
|
+
const servers = externalCatalog({ dataHome, ...externalContext }).map(row => ({ name: row.ref.slice(4), blocked: row.blocked, deliverable: Object.values(row.providers).includes('deliverable') }));
|
|
34
|
+
selectExternalServers(items, { servers });
|
|
35
|
+
}
|
|
36
|
+
const tools = writeDefaultTools(dataHome, items);
|
|
37
|
+
log(`Default tools: ${JSON.stringify(tools)}. Applies to future runtimes; running sessions keep their own list.`);
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
throw new Error('Usage: synabun tools [list | get | set <ids>]');
|
|
41
|
+
} catch (err) {
|
|
42
|
+
error(`${err.code ? `${err.code}: ` : ''}${err.message}`);
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
}
|