synabun 2.0.0 → 2.0.5
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 +57 -0
- package/NOTICE +5 -0
- package/README.md +1 -1
- package/THIRD-PARTY-LICENSES.md +46 -0
- package/benchmarks/fixtures/browser/README.md +1 -1
- package/benchmarks/fixtures/desktop/README.md +1 -1
- package/docs/whatsapp.md +2 -2
- package/hooks/claude-code/pre-compact.mjs +11 -0
- package/hooks/claude-code/prompt-submit.mjs +60 -184
- package/hooks/claude-code/session-start.mjs +73 -16
- package/hooks/claude-code/shared.mjs +20 -0
- package/hooks/claude-code/stop.mjs +5 -5
- package/lib/start-launcher.js +88 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -3
- package/neural-interface/i18n/en.json +1500 -153
- package/neural-interface/i18n/pt-BR.json +1498 -152
- package/neural-interface/lib/assistant-api.js +62 -6
- package/neural-interface/lib/assistant-brains/claude.js +4 -0
- package/neural-interface/lib/assistant-catalog.js +179 -27
- package/neural-interface/lib/assistant-config.js +31 -2
- package/neural-interface/lib/assistant-dispatch.js +62 -13
- package/neural-interface/lib/assistant-hidden-models.js +393 -66
- package/neural-interface/lib/assistant-memory.js +24 -10
- package/neural-interface/lib/assistant-persona.js +6 -2
- package/neural-interface/lib/assistant-pricing.js +21 -6
- package/neural-interface/lib/assistant-runtime.js +181 -12
- package/neural-interface/lib/assistant-task-prompt.js +13 -3
- package/neural-interface/lib/assistant-test-density.js +142 -0
- package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
- package/neural-interface/lib/assistant-user-context.js +908 -0
- package/neural-interface/lib/assistant-user-knowledge.js +328 -0
- package/neural-interface/lib/assistant-user-learn.js +404 -0
- package/neural-interface/lib/assistant-user-threads.js +226 -0
- package/neural-interface/lib/assistant-user-writer.js +181 -0
- package/neural-interface/lib/browser-background-tab.js +134 -0
- package/neural-interface/lib/browser-screenshot.js +678 -61
- package/neural-interface/lib/claude-agent-bridge.js +33 -6
- package/neural-interface/lib/claude-model-catalog.js +30 -2
- package/neural-interface/lib/claude-temporary.js +46 -7
- package/neural-interface/lib/cloudflared/install.js +533 -0
- package/neural-interface/lib/cloudflared/manager.js +545 -0
- package/neural-interface/lib/cloudflared/pins.js +115 -0
- package/neural-interface/lib/cloudflared/pins.json +63 -0
- package/neural-interface/lib/cloudflared/tunnel.js +265 -0
- package/neural-interface/lib/editor-completion-endpoint.js +264 -0
- package/neural-interface/lib/editor-completion-opencode.js +436 -0
- package/neural-interface/lib/editor-completion.js +714 -0
- package/neural-interface/lib/editor-config.js +149 -0
- package/neural-interface/lib/editor-files-api.js +268 -0
- package/neural-interface/lib/editor-git-api.js +134 -0
- package/neural-interface/lib/file-explorer-api.js +1039 -0
- package/neural-interface/lib/file-explorer-system.js +355 -0
- package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
- package/neural-interface/lib/file-explorer-watch.js +239 -0
- package/neural-interface/lib/git-diff-api.js +103 -0
- package/neural-interface/lib/git-read.js +202 -0
- package/neural-interface/lib/greeting/api.js +89 -0
- package/neural-interface/lib/greeting/budgets.js +90 -0
- package/neural-interface/lib/greeting/engine.js +461 -0
- package/neural-interface/lib/greeting/facts.js +320 -0
- package/neural-interface/lib/greeting/sources.js +252 -0
- package/neural-interface/lib/greeting/writer.js +139 -0
- package/neural-interface/lib/http-guards.js +189 -13
- package/neural-interface/lib/machine-names.js +88 -0
- package/neural-interface/lib/memory-api.js +31 -7
- package/neural-interface/lib/native-loop-providers.js +10 -5
- package/neural-interface/lib/plugin-installer.js +4 -2
- package/neural-interface/lib/project-storage.js +5 -7
- package/neural-interface/lib/request-gate.js +589 -0
- package/neural-interface/lib/ui-blob-api.js +108 -0
- package/neural-interface/lib/ui-blob-store.js +68 -0
- package/neural-interface/package-lock.json +701 -2
- package/neural-interface/package.json +13 -2
- package/neural-interface/public/install-app.html +9 -6
- package/neural-interface/public/offline.html +3 -2
- package/neural-interface/public/onboarding.html +12 -1
- package/neural-interface/public/shared/agent-runtime-options.js +22 -6
- package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +312 -62
- package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
- package/neural-interface/public/shared/assistant/asst-mascot.js +18 -58
- package/neural-interface/public/shared/assistant/asst-menu.js +12 -8
- package/neural-interface/public/shared/assistant/asst-panel.js +150 -28
- package/neural-interface/public/shared/assistant/asst-render.js +162 -19
- package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
- package/neural-interface/public/shared/assistant/asst-route.js +23 -329
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +495 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
- package/neural-interface/public/shared/assistant/asst-state.js +185 -1
- package/neural-interface/public/shared/assistant/asst-styles.js +158 -4
- package/neural-interface/public/shared/blob-store.js +111 -22
- package/neural-interface/public/shared/cdx/cdx-panel.js +32 -11
- package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
- package/neural-interface/public/shared/cdx/cdx-styles.js +36 -0
- package/neural-interface/public/shared/cdx/cdx-tabs.js +87 -4
- package/neural-interface/public/shared/constants.js +6 -1
- package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +15 -0
- package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
- package/neural-interface/public/shared/editor/editor-ai.js +619 -0
- package/neural-interface/public/shared/editor/editor-commands.js +212 -0
- package/neural-interface/public/shared/editor/editor-compare.js +103 -0
- package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
- package/neural-interface/public/shared/editor/editor-doc.js +257 -0
- package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
- package/neural-interface/public/shared/editor/editor-git.js +95 -0
- package/neural-interface/public/shared/editor/editor-languages.js +156 -0
- package/neural-interface/public/shared/editor/editor-lint.js +229 -0
- package/neural-interface/public/shared/editor/editor-loader.js +51 -0
- package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
- package/neural-interface/public/shared/editor/editor-menu.js +233 -0
- package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
- package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
- package/neural-interface/public/shared/editor/editor-palette.js +125 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
- package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
- package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
- package/neural-interface/public/shared/editor/editor-safety.js +500 -0
- package/neural-interface/public/shared/editor/editor-search.js +352 -0
- package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
- package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
- package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
- package/neural-interface/public/shared/editor/editor-theme.js +244 -0
- package/neural-interface/public/shared/editor/editor-view.js +332 -0
- package/neural-interface/public/shared/explorer/fx-api.js +216 -0
- package/neural-interface/public/shared/explorer/fx-links.js +204 -0
- package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
- package/neural-interface/public/shared/explorer/fx-main.js +865 -0
- package/neural-interface/public/shared/explorer/fx-names.js +87 -0
- package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
- package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
- package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
- package/neural-interface/public/shared/file-links.js +339 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
- package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
- package/neural-interface/public/shared/html-shell.js +160 -165
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +99 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +5 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +40 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
- package/neural-interface/public/shared/pwa-install-page.js +29 -1
- package/neural-interface/public/shared/pwa-install.css +3 -3
- package/neural-interface/public/shared/pwa-install.js +100 -1
- package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
- package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
- package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
- package/neural-interface/public/shared/settings/settings-ia.js +18 -4
- package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
- package/neural-interface/public/shared/start-bridge.js +238 -19
- package/neural-interface/public/shared/styles.css +1414 -721
- package/neural-interface/public/shared/synabun-mascot.js +20 -56
- package/neural-interface/public/shared/tunnel-view.js +167 -0
- package/neural-interface/public/shared/ui-claude-panel.js +157 -16
- package/neural-interface/public/shared/ui-detail.js +15 -0
- package/neural-interface/public/shared/ui-explorer.js +22 -0
- package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
- package/neural-interface/public/shared/ui-file-links.js +103 -0
- package/neural-interface/public/shared/ui-help.js +26 -0
- package/neural-interface/public/shared/ui-invite.js +126 -116
- package/neural-interface/public/shared/ui-keybinds.js +4 -0
- package/neural-interface/public/shared/ui-loading.js +159 -15
- package/neural-interface/public/shared/ui-menubar.js +12 -30
- package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
- package/neural-interface/public/shared/ui-navbar.js +45 -21
- package/neural-interface/public/shared/ui-settings.js +455 -488
- package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
- package/neural-interface/public/shared/ui-sync.js +16 -0
- package/neural-interface/public/shared/ui-terminal.js +23 -5
- package/neural-interface/public/shared/ui-tunnel.js +130 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
- package/neural-interface/public/shared/ui-whiteboard.js +12 -0
- package/neural-interface/public/shared/ui-worker-end.js +245 -0
- package/neural-interface/public/shared/ui-workspaces.js +41 -27
- package/neural-interface/public/shared/worker-end.js +251 -0
- package/neural-interface/public/sw.js +2 -2
- package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
- package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
- package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
- package/neural-interface/scripts/build-vendor.mjs +78 -2
- package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
- package/neural-interface/server.js +873 -1028
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/package.json +2 -8
- package/setup.js +44 -7
- package/benchmarks/browser-live-results.json +0 -79
- package/benchmarks/browser-v2-results.json +0 -258
- package/neural-interface/lib/codex-capabilities.d.ts +0 -41
- package/neural-interface/public/category-logos/claude.svg +0 -1
- package/neural-interface/public/category-logos/gemini-color.svg +0 -1
- package/neural-interface/public/category-logos/openai.svg +0 -1
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude.svg +0 -1
- package/neural-interface/public/gallery.svg +0 -6
- package/neural-interface/public/leonardoai.svg +0 -1
- package/neural-interface/public/mcp.svg +0 -4
- package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
- package/neural-interface/public/pin-svgrepo-com.svg +0 -25
- package/neural-interface/public/sessionicon.svg +0 -8
- package/neural-interface/public/toolbar-album.svg +0 -8
- package/neural-interface/public/toolbar-question.svg +0 -6
- package/neural-interface/public/toolbar-sessions.svg +0 -6
- package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SETTINGS — the Assistant's user context, in words
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What `GET /api/assistant/user-context` (lib/assistant-api.js, the engine in lib/assistant-user-context.js)
|
|
6
|
+
// says about Settings → Automations → Automation behavior: whether the three switches can be used at all,
|
|
7
|
+
// what each one is set to, what stands in the way of a layer, and whether the digest is in use. Pure: no DOM
|
|
8
|
+
// and no import, so node runs it (tests/settings-user-context-view.test.mjs).
|
|
9
|
+
//
|
|
10
|
+
// readUserContextResponse({ status, body, error }) what the server answered → a reading
|
|
11
|
+
// describeUserContext(reading, { tx }) the section as ui-settings.js draws it
|
|
12
|
+
// describeUserContextSaveFailure(input, { tx }) the sentence for a switch that could not be saved
|
|
13
|
+
//
|
|
14
|
+
// `tx(key, params)` is the caller's translate function over the settings.redesign.* strings (the kit's `tx`).
|
|
15
|
+
//
|
|
16
|
+
// The section never shows a healthy-looking control for something that is not happening: a server without
|
|
17
|
+
// the engine gets disabled rows and says why, a switched-off layer says what stops, and the digest is shown
|
|
18
|
+
// as told to a new conversation only while "Knowledge of you" is on (the engine's preview is built either way).
|
|
19
|
+
|
|
20
|
+
const K = 'settings.redesign.hooks.uc';
|
|
21
|
+
|
|
22
|
+
/** The three layers, in the order the section lists them. */
|
|
23
|
+
export const USER_CONTEXT_LAYERS = Object.freeze(['knowledge', 'nudges', 'trace']);
|
|
24
|
+
|
|
25
|
+
const clean = (value, max = 200) => {
|
|
26
|
+
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
27
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* What the server answered.
|
|
32
|
+
*
|
|
33
|
+
* ready the engine is there: `payload` is { settings, capabilities, preview }
|
|
34
|
+
* old-server 404: the running server was started before the engine existed (public/ reloads at any
|
|
35
|
+
* time, lib/ only on a restart)
|
|
36
|
+
* failed 503 USER_CONTEXT_UNAVAILABLE: the engine did not start at boot; `reason` is the error
|
|
37
|
+
* unreadable anything else (no answer, a refusal, a body that is not the payload); `reason` says what
|
|
38
|
+
*
|
|
39
|
+
* @param {{ status?: number, body?: object|null, error?: string|null }} response
|
|
40
|
+
* @returns {{ state: 'ready'|'old-server'|'failed'|'unreadable', payload: object|null, reason: string|null }}
|
|
41
|
+
*/
|
|
42
|
+
export function readUserContextResponse({ status = 0, body = null, error = null } = {}) {
|
|
43
|
+
if (status === 200 && body?.ok === true && body.settings && typeof body.settings === 'object') return { state: 'ready', payload: body, reason: null };
|
|
44
|
+
if (status === 404) return { state: 'old-server', payload: null, reason: null };
|
|
45
|
+
if (status === 503 && body?.code === 'USER_CONTEXT_UNAVAILABLE') return { state: 'failed', payload: null, reason: clean(body.reason) || null };
|
|
46
|
+
return { state: 'unreadable', payload: null, reason: clean(body?.error || error) || (status ? `HTTP ${status}` : null) };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The settings as booleans, the way the engine reads them: anything that is not exactly `false` is on. */
|
|
50
|
+
const layersOf = (settings) => Object.fromEntries(USER_CONTEXT_LAYERS.map((key) => [key, settings?.[key] !== false]));
|
|
51
|
+
|
|
52
|
+
// Why learning cannot run → the sentence, and whether Settings → AI decisions is where to fix it.
|
|
53
|
+
const LEARNING_REASONS = {
|
|
54
|
+
'kill-switch': { key: 'learn.killSwitch', link: false },
|
|
55
|
+
'master-off': { key: 'learn.masterOff', link: 'jv-sec-master' },
|
|
56
|
+
'no-key': { key: 'learn.noKey', link: 'jv-sec-connection' },
|
|
57
|
+
'surface-off': { key: 'learn.surfaceOff', link: 'jv-sec-surfaces' },
|
|
58
|
+
waiting: { key: 'learn.waiting', link: false },
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The section as it is drawn.
|
|
63
|
+
*
|
|
64
|
+
* `note`: one line above the switches when they cannot be used (and why), else null.
|
|
65
|
+
* `layers[key]`: `on` (the stored value; false while it cannot be read), `disabled`, and `notes`: what stands
|
|
66
|
+
* in the way of that layer right now, each `{ text, tone: 'warn'|'info', link }` where `link` is
|
|
67
|
+
* `{ label, target }` (a Settings deep link) or null.
|
|
68
|
+
* `applies`: the line that says when a change takes effect, or ''.
|
|
69
|
+
* `digest`: `shown` (false without the engine), `state` 'in-use' | 'empty' | 'off', the `count` text, the `rows`
|
|
70
|
+
* to list (none while off), the `message` shown in place of rows (or ''), and the `omitted` line (or '').
|
|
71
|
+
*
|
|
72
|
+
* @param {{ state: string, payload: object|null, reason: string|null }} reading
|
|
73
|
+
* @param {{ tx: (key: string, params?: object) => string }} deps
|
|
74
|
+
*/
|
|
75
|
+
export function describeUserContext(reading, { tx }) {
|
|
76
|
+
const state = reading?.state || 'unreadable';
|
|
77
|
+
const ready = state === 'ready';
|
|
78
|
+
const page = tx('settings.redesign.page.ai_decisions.title');
|
|
79
|
+
const view = {
|
|
80
|
+
state, ready, note: null, applies: ready ? tx(`${K}.applies`) : '',
|
|
81
|
+
layers: Object.fromEntries(USER_CONTEXT_LAYERS.map((key) => [key, { on: false, disabled: !ready, notes: [] }])),
|
|
82
|
+
digest: { shown: false, state: 'off', count: '', rows: [], message: '', omitted: '' },
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
if (!ready) {
|
|
86
|
+
const reason = clean(reading?.reason);
|
|
87
|
+
if (state === 'old-server') view.note = { text: tx(`${K}.restart`), tone: 'warn' };
|
|
88
|
+
else if (state === 'failed') view.note = { text: reason ? tx(`${K}.failed`, { reason }) : tx(`${K}.failedNoReason`), tone: 'warn' };
|
|
89
|
+
else view.note = { text: reason ? tx(`${K}.unreadable`, { reason }) : tx(`${K}.unreadableNoReason`), tone: 'warn' };
|
|
90
|
+
return view;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const { settings, capabilities, preview } = reading.payload;
|
|
94
|
+
const on = layersOf(settings);
|
|
95
|
+
const caps = capabilities && typeof capabilities === 'object' ? capabilities : {};
|
|
96
|
+
for (const key of USER_CONTEXT_LAYERS) view.layers[key].on = on[key];
|
|
97
|
+
|
|
98
|
+
// Without the memory store nothing is read, learned or recorded: both layers that live in it say so.
|
|
99
|
+
const noStore = caps.store === false;
|
|
100
|
+
if (noStore) {
|
|
101
|
+
if (on.knowledge) view.layers.knowledge.notes.push({ text: tx(`${K}.noStore`), tone: 'warn', link: null });
|
|
102
|
+
if (on.nudges) view.layers.nudges.notes.push({ text: tx(`${K}.noStore`), tone: 'warn', link: null });
|
|
103
|
+
}
|
|
104
|
+
// Learning needs Jev on the `user-learning` surface; the digest does not.
|
|
105
|
+
if (on.knowledge && !noStore && caps.jev === false) {
|
|
106
|
+
const known = LEARNING_REASONS[caps.jevReason] || { key: 'learn.unavailable', link: false };
|
|
107
|
+
view.layers.knowledge.notes.push({
|
|
108
|
+
text: tx(`${K}.${known.key}`, { page }), tone: 'warn',
|
|
109
|
+
link: known.link ? { label: tx(`${K}.openPage`, { page }), target: { tab: 'ai-decisions', scrollTo: known.link } } : null,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// The statement writer: without it (or when its last one-shot failed) a statement stays in the user's own words.
|
|
113
|
+
if (on.knowledge && !noStore && caps.jev !== false) {
|
|
114
|
+
if (caps.writer === false) view.layers.knowledge.notes.push({ text: tx(`${K}.writer.missing`), tone: 'info', link: null });
|
|
115
|
+
else if (caps.writerLast && caps.writerLast.ok === false) {
|
|
116
|
+
const reason = clean(caps.writerLast.reason, 120);
|
|
117
|
+
view.layers.knowledge.notes.push({ text: reason ? tx(`${K}.writer.failed`, { reason }) : tx(`${K}.writer.failedNoReason`), tone: 'info', link: null });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// The trace is a line about the two layers: with both off the engine emits nothing.
|
|
121
|
+
if (on.trace && !on.knowledge && !on.nudges) view.layers.trace.notes.push({ text: tx(`${K}.trace.nothing`), tone: 'info', link: null });
|
|
122
|
+
|
|
123
|
+
// The digest: what a new conversation is told, which is nothing while the layer is off.
|
|
124
|
+
const rows = Array.isArray(preview?.rows) ? preview.rows.filter((row) => row && row.id && row.statement) : [];
|
|
125
|
+
const omitted = Number(preview?.omitted) > 0 ? Math.round(Number(preview.omitted)) : 0;
|
|
126
|
+
view.digest.shown = true;
|
|
127
|
+
if (!on.knowledge) {
|
|
128
|
+
view.digest.state = 'off';
|
|
129
|
+
view.digest.count = tx('settings.redesign.common.off');
|
|
130
|
+
view.digest.message = tx(`${K}.off.note`);
|
|
131
|
+
} else {
|
|
132
|
+
view.digest.state = rows.length ? 'in-use' : 'empty';
|
|
133
|
+
view.digest.count = tx(`${K}.tokens`, { tokens: Math.max(0, Math.round(Number(preview?.tokens) || 0)), max: Math.round(Number(preview?.maxTokens) || 300) });
|
|
134
|
+
view.digest.rows = rows.map((row) => ({ id: String(row.id), statement: String(row.statement), facet: String(row.facet || ''), seed: row.seed === true }));
|
|
135
|
+
view.digest.message = rows.length ? '' : tx(`${K}.empty`);
|
|
136
|
+
view.digest.omitted = omitted > 0 ? tx(`${K}.omitted`, { n: omitted }) : '';
|
|
137
|
+
}
|
|
138
|
+
return view;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A switch that could not be saved: it goes back to where it was, and this is the sentence that says so.
|
|
143
|
+
*
|
|
144
|
+
* @param {{ name: string, wanted: boolean, status?: number, body?: object|null, error?: string|null }} input
|
|
145
|
+
* `name` is the switch's label, `wanted` what the click asked for
|
|
146
|
+
* @returns {string}
|
|
147
|
+
*/
|
|
148
|
+
export function describeUserContextSaveFailure({ name, wanted, status = 0, body = null, error = null } = {}, { tx }) {
|
|
149
|
+
let reason = clean(body?.error || body?.message, 160);
|
|
150
|
+
if (!reason && status === 404) reason = tx(`${K}.save.oldServer`);
|
|
151
|
+
if (!reason && status) reason = `HTTP ${status}`;
|
|
152
|
+
if (!reason) reason = clean(error, 160) || tx(`${K}.save.noAnswer`);
|
|
153
|
+
return tx(`${K}.save.${wanted ? 'on' : 'off'}`, { name, reason });
|
|
154
|
+
}
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
// the time it happened), and its own probe of the server. Nothing is guessed:
|
|
13
13
|
// no percentages, and no step is named before the launch reported it.
|
|
14
14
|
//
|
|
15
|
+
// A restart the user asked for is shown by the same bridge (`restart()`): the
|
|
16
|
+
// screen is drawn in the click, and what it says comes from the same three
|
|
17
|
+
// things, plus the server's own word that it is shutting down. The supervisor
|
|
18
|
+
// that brings the server back reports on the same beacon (`reason: 'restart'`).
|
|
19
|
+
//
|
|
15
20
|
// offline.html is shown when nothing can be fetched, so the server pastes this
|
|
16
21
|
// file into it (lib/offline-page.js drops the `export` keywords). Keep every
|
|
17
22
|
// export a plain `export function` / `export const` at the start of a line,
|
|
@@ -37,6 +42,7 @@ export const START_LIMITS = {
|
|
|
37
42
|
lostAfterMs: 3000, // a launch that was reporting went silent: stop repeating what it said
|
|
38
43
|
idleBeaconChecks: 2, // on load, look for a start already under way (a reload in the middle of one)
|
|
39
44
|
reloadDelayMs: 150, // the "it is up" frame before the page reloads
|
|
45
|
+
restartGraceMs: 15000, // a restart was asked and the server neither went away nor said it is going: stop waiting
|
|
40
46
|
beaconTimeoutMs: 1200,
|
|
41
47
|
};
|
|
42
48
|
|
|
@@ -152,7 +158,7 @@ export function startBeaconTarget(loc) {
|
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
const START_LAUNCH_STATES = ['starting', 'started', 'failed', 'gave-up'];
|
|
155
|
-
const START_LAUNCH_STEPS = ['received', 'locked', 'terminal', 'spawned'];
|
|
161
|
+
const START_LAUNCH_STEPS = ['received', 'locked', 'terminal', 'spawned', 'exited'];
|
|
156
162
|
const START_SUPERVISOR_PHASES = ['checking', 'dependencies', 'browser', 'build', 'snapshot', 'server', 'listening', 'failed'];
|
|
157
163
|
|
|
158
164
|
function startCleanError(error) {
|
|
@@ -217,6 +223,8 @@ export function readStartBeacon(data, { port, askedAt = null, following = null,
|
|
|
217
223
|
const newest = Math.max(last.at, supervisor ? supervisor.at : 0, startedAt);
|
|
218
224
|
return {
|
|
219
225
|
id,
|
|
226
|
+
// 'restart': a supervisor bringing its server back, not a launcher starting one.
|
|
227
|
+
reason: data.reason === 'restart' ? 'restart' : 'start',
|
|
220
228
|
state: data.state,
|
|
221
229
|
startedAt,
|
|
222
230
|
step: last.id,
|
|
@@ -251,7 +259,10 @@ export function fetchStartBeacon(target, { fetchImpl, timeoutMs = START_LIMITS.b
|
|
|
251
259
|
* idle → launching → starting | waiting → stalled | failed, or online at any
|
|
252
260
|
* point. The page keeps probing in every state, so a server started by hand is
|
|
253
261
|
* noticed just the same.
|
|
254
|
-
* probe() → Promise<boolean> is the server
|
|
262
|
+
* probe() → Promise<boolean | { up, boot, stopping }> is the server
|
|
263
|
+
* answering? `boot` names the server that answered and
|
|
264
|
+
* `stopping` is its own word that it is shutting down for a
|
|
265
|
+
* restart; a server that says neither is just `true`.
|
|
255
266
|
* launch() → hand START_LINK to the OS
|
|
256
267
|
* beacon() → Promise<object|null> the launch's raw report (optional)
|
|
257
268
|
* port → the server's port (what a beacon must be about)
|
|
@@ -267,6 +278,13 @@ export function fetchStartBeacon(target, { fetchImpl, timeoutMs = START_LIMITS.b
|
|
|
267
278
|
* 'stalled' stallAfterMs with no word, or the launcher gave up waiting
|
|
268
279
|
* 'failed' the launcher reports that the start failed, and why
|
|
269
280
|
* 'online' the server answers this page
|
|
281
|
+
*
|
|
282
|
+
* A restart (`restart()`) adds two:
|
|
283
|
+
* 'restarting' the server was asked to restart, or said it is restarting;
|
|
284
|
+
* it has not come back and no supervisor reports on it yet
|
|
285
|
+
* 'refused' the restart did not happen and the server still answers
|
|
286
|
+
* While one is under way `snapshot().restart` says what is known of it, and
|
|
287
|
+
* 'online' means a server other than the one that was asked answers.
|
|
270
288
|
*/
|
|
271
289
|
export function createStartBridge({
|
|
272
290
|
probe,
|
|
@@ -292,9 +310,13 @@ export function createStartBridge({
|
|
|
292
310
|
let launchSeenAt = 0;
|
|
293
311
|
let idleChecks = 0;
|
|
294
312
|
let shown = '';
|
|
313
|
+
// The restart under way, on this page's clock: when it was asked for (or
|
|
314
|
+
// this page was told), when the server said it is restarting, when it first
|
|
315
|
+
// did not answer, and the server that is being replaced.
|
|
316
|
+
let restart = null;
|
|
295
317
|
const past = [];
|
|
296
318
|
|
|
297
|
-
const busy = () => state === 'launching' || state === 'starting' || state === 'waiting';
|
|
319
|
+
const busy = () => state === 'launching' || state === 'starting' || state === 'waiting' || state === 'restarting';
|
|
298
320
|
|
|
299
321
|
function snapshot() {
|
|
300
322
|
return {
|
|
@@ -304,15 +326,24 @@ export function createStartBridge({
|
|
|
304
326
|
since,
|
|
305
327
|
elapsedMs: since === null ? 0 : Math.max(0, now() - since),
|
|
306
328
|
launch: launchInfo,
|
|
329
|
+
restart: restart ? {
|
|
330
|
+
asked: restart.asked,
|
|
331
|
+
askedAt: restart.askedAt,
|
|
332
|
+
acceptedAt: restart.acceptedAt,
|
|
333
|
+
downAt: restart.downAt,
|
|
334
|
+
refused: restart.refused,
|
|
335
|
+
} : null,
|
|
307
336
|
};
|
|
308
337
|
}
|
|
309
338
|
|
|
310
339
|
function update() {
|
|
311
340
|
const snap = snapshot();
|
|
312
341
|
const l = snap.launch;
|
|
342
|
+
const r = snap.restart;
|
|
313
343
|
const key = [
|
|
314
344
|
snap.state, snap.clicks, busy() ? Math.floor(snap.elapsedMs / 1000) : '',
|
|
315
345
|
l ? [l.id, l.state, l.step, l.supervisor?.phase, l.supervisor?.detail?.step, l.supervisor?.detail?.files, l.quietMs >= lim.quietAfterMs, l.error?.message].join('|') : '',
|
|
346
|
+
r ? [r.acceptedAt !== null, r.downAt !== null, r.refused?.code].join('|') : '',
|
|
316
347
|
].join('/');
|
|
317
348
|
if (key === shown) return;
|
|
318
349
|
shown = key;
|
|
@@ -326,14 +357,65 @@ export function createStartBridge({
|
|
|
326
357
|
}
|
|
327
358
|
|
|
328
359
|
function schedule(delay) {
|
|
329
|
-
if (stopped || state === 'online') return;
|
|
360
|
+
if (stopped || state === 'online' || state === 'refused') return;
|
|
330
361
|
if (timer) clearTimer(timer);
|
|
331
362
|
timer = setTimer(tick, delay ?? (busy() ? lim.pollMs : lim.idlePollMs));
|
|
332
363
|
}
|
|
333
364
|
|
|
365
|
+
/** One probe answer as { up, boot, stopping }; a bare `true` is a server that says nothing about itself. */
|
|
366
|
+
function readProbe(value) {
|
|
367
|
+
if (value === true) return { up: true, boot: '', stopping: false };
|
|
368
|
+
if (value && typeof value === 'object' && value.up === true) {
|
|
369
|
+
return { up: true, boot: typeof value.boot === 'string' ? value.boot : '', stopping: value.stopping === true };
|
|
370
|
+
}
|
|
371
|
+
return { up: false, boot: '', stopping: false };
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** The restart did not happen and the server is still there: say why and stop. */
|
|
375
|
+
function refuse(why) {
|
|
376
|
+
restart.refused = why;
|
|
377
|
+
set('refused');
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* During a restart an answer is not yet the server being back: the one that
|
|
382
|
+
* was asked keeps answering while it shuts down. True only when the server
|
|
383
|
+
* that answers is another one. A server that names itself (`boot`) settles
|
|
384
|
+
* it; one that does not (an older server) is believed once it was seen gone.
|
|
385
|
+
*/
|
|
386
|
+
function judgeRestart(seen) {
|
|
387
|
+
const r = restart;
|
|
388
|
+
if (!seen.up) {
|
|
389
|
+
if (r.downAt === null) r.downAt = now();
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
if (seen.stopping) {
|
|
393
|
+
// Its own word: it is the one being replaced, and it is on its way out.
|
|
394
|
+
if (r.acceptedAt === null) r.acceptedAt = now();
|
|
395
|
+
if (!r.boot && seen.boot) r.boot = seen.boot;
|
|
396
|
+
r.downAt = null; // an answer it missed was not it stopping
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
const named = !!r.boot && !!seen.boot;
|
|
400
|
+
if (named && seen.boot !== r.boot) return true;
|
|
401
|
+
if (!named && r.downAt !== null) return true;
|
|
402
|
+
// The same server (or one that cannot be told apart), not shutting down.
|
|
403
|
+
if (named) r.downAt = null;
|
|
404
|
+
if (r.pending) return false; // the request is still on its way
|
|
405
|
+
if (r.rejected) { refuse(r.rejected); return false; }
|
|
406
|
+
if (r.unanswered) { refuse({ code: 'unreachable', status: 0, message: '' }); return false; }
|
|
407
|
+
if (r.acceptedAt !== null && now() - r.acceptedAt >= lim.restartGraceMs) {
|
|
408
|
+
if (named) { refuse({ code: 'still-running', status: 0, message: '' }); return false; }
|
|
409
|
+
return true; // it tells nothing about itself: load it again and see
|
|
410
|
+
}
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
|
|
334
414
|
/** What the launch says, when there is one to listen to right now. */
|
|
335
415
|
async function listen() {
|
|
336
416
|
if (!beacon) return;
|
|
417
|
+
// A restart: nobody is starting anything while the old server still answers.
|
|
418
|
+
if (restart && restart.downAt === null) return;
|
|
337
419
|
// Idle: only just after load, for a start that was already under way.
|
|
338
420
|
if (state === 'idle' && idleChecks >= lim.idleBeaconChecks) return;
|
|
339
421
|
if (state === 'idle') idleChecks++;
|
|
@@ -357,7 +439,8 @@ export function createStartBridge({
|
|
|
357
439
|
// reading how. What it said last is no longer known to be true.
|
|
358
440
|
if (state === 'starting' && now() - launchSeenAt >= lim.lostAfterMs) {
|
|
359
441
|
launchInfo = null;
|
|
360
|
-
if (
|
|
442
|
+
if (restart) set('restarting'); // back to what this page sees for itself
|
|
443
|
+
else if (askedAt === null) { since = null; set('idle'); } // a start this page never asked for: back to watching
|
|
361
444
|
else set(now() - askedAt >= lim.hintAfterMs ? 'waiting' : 'launching');
|
|
362
445
|
}
|
|
363
446
|
}
|
|
@@ -366,17 +449,22 @@ export function createStartBridge({
|
|
|
366
449
|
timer = null;
|
|
367
450
|
if (stopped || probing) return;
|
|
368
451
|
probing = true;
|
|
369
|
-
let
|
|
370
|
-
try {
|
|
371
|
-
|
|
452
|
+
let seen = readProbe(false);
|
|
453
|
+
try { seen = readProbe(await probe()); } catch {}
|
|
454
|
+
let up = seen.up && !seen.stopping; // a server on its way out is not one to connect to
|
|
455
|
+
if (restart && !stopped && state !== 'refused') up = judgeRestart(seen);
|
|
456
|
+
if (!up && !stopped && state !== 'refused') await listen();
|
|
372
457
|
probing = false;
|
|
373
458
|
if (stopped) return;
|
|
374
459
|
if (up) { set('online'); update(); return; }
|
|
460
|
+
if (state === 'refused') { update(); return; }
|
|
375
461
|
if (state === 'launching' || state === 'waiting') {
|
|
376
462
|
const waited = now() - askedAt;
|
|
377
463
|
if (waited >= lim.stallAfterMs) set('stalled');
|
|
378
464
|
else if (waited >= lim.hintAfterMs) set('waiting');
|
|
379
465
|
}
|
|
466
|
+
// Gone, and nobody reports bringing it back: the button and the command return.
|
|
467
|
+
if (state === 'restarting' && restart.downAt !== null && now() - restart.downAt >= lim.stallAfterMs) set('stalled');
|
|
380
468
|
update();
|
|
381
469
|
schedule();
|
|
382
470
|
}
|
|
@@ -388,13 +476,14 @@ export function createStartBridge({
|
|
|
388
476
|
watch() { schedule(0); },
|
|
389
477
|
/** The click. Ignored while a start is already under way. */
|
|
390
478
|
start() {
|
|
391
|
-
if (stopped || state === 'online' || busy()) return false;
|
|
479
|
+
if (stopped || state === 'online' || state === 'refused' || busy()) return false;
|
|
392
480
|
clicks++;
|
|
393
481
|
askedAt = now();
|
|
394
482
|
since = askedAt;
|
|
395
483
|
// What the last try reported is over: a launcher still saying so is not this one.
|
|
396
484
|
if (launchInfo && !past.includes(launchInfo.id)) past.push(launchInfo.id);
|
|
397
485
|
launchInfo = null;
|
|
486
|
+
restart = null; // a restart that did not bring it back is over: this is a start
|
|
398
487
|
state = 'idle'; // a second try after 'stalled' announces 'launching' again
|
|
399
488
|
set('launching');
|
|
400
489
|
update();
|
|
@@ -402,6 +491,67 @@ export function createStartBridge({
|
|
|
402
491
|
schedule();
|
|
403
492
|
return true;
|
|
404
493
|
},
|
|
494
|
+
/**
|
|
495
|
+
* A restart of the server. Call it from the click itself: the first
|
|
496
|
+
* snapshot is handed to onUpdate before anything is asked of the network.
|
|
497
|
+
* request() → Promise<{ ok, boot, status, message }> the asking (this
|
|
498
|
+
* page's own click); leave it out when the page is only told
|
|
499
|
+
* announced → the server itself said it is restarting (`boot`: which one)
|
|
500
|
+
* A request that fails counts as refused only once the server is seen to
|
|
501
|
+
* be still there. Returns false when a restart is already being shown; an
|
|
502
|
+
* announcement then still counts as the server's word.
|
|
503
|
+
*/
|
|
504
|
+
restart({ request = null, announced = false, boot = '' } = {}) {
|
|
505
|
+
if (stopped || state === 'online') return false;
|
|
506
|
+
if (restart) {
|
|
507
|
+
if (announced && !restart.refused) {
|
|
508
|
+
if (restart.acceptedAt === null) restart.acceptedAt = now();
|
|
509
|
+
if (!restart.boot && boot) restart.boot = String(boot);
|
|
510
|
+
update();
|
|
511
|
+
}
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
if (typeof request !== 'function' && !announced) return false;
|
|
515
|
+
askedAt = now();
|
|
516
|
+
since = askedAt;
|
|
517
|
+
if (launchInfo && !past.includes(launchInfo.id)) past.push(launchInfo.id);
|
|
518
|
+
launchInfo = null;
|
|
519
|
+
const mine = restart = {
|
|
520
|
+
asked: typeof request === 'function',
|
|
521
|
+
askedAt,
|
|
522
|
+
acceptedAt: announced ? askedAt : null,
|
|
523
|
+
downAt: null,
|
|
524
|
+
boot: announced && boot ? String(boot) : '',
|
|
525
|
+
pending: typeof request === 'function',
|
|
526
|
+
rejected: null,
|
|
527
|
+
unanswered: false,
|
|
528
|
+
refused: null,
|
|
529
|
+
};
|
|
530
|
+
state = 'idle';
|
|
531
|
+
set('restarting');
|
|
532
|
+
update();
|
|
533
|
+
if (mine.asked) {
|
|
534
|
+
Promise.resolve().then(() => request()).then((answer) => {
|
|
535
|
+
if (stopped || restart !== mine) return;
|
|
536
|
+
mine.pending = false;
|
|
537
|
+
if (answer && answer.ok === true) {
|
|
538
|
+
if (mine.acceptedAt === null) mine.acceptedAt = now();
|
|
539
|
+
if (answer.boot) mine.boot = String(answer.boot);
|
|
540
|
+
} else {
|
|
541
|
+
mine.rejected = { code: 'refused', status: Number(answer?.status) || 0, message: String(answer?.message || '').slice(0, 200) };
|
|
542
|
+
}
|
|
543
|
+
update();
|
|
544
|
+
schedule(0); // look at once: a refusal is only one when the server still answers
|
|
545
|
+
}, () => {
|
|
546
|
+
if (stopped || restart !== mine) return;
|
|
547
|
+
mine.pending = false;
|
|
548
|
+
mine.unanswered = true;
|
|
549
|
+
schedule(0);
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
schedule();
|
|
553
|
+
return true;
|
|
554
|
+
},
|
|
405
555
|
stop() {
|
|
406
556
|
stopped = true;
|
|
407
557
|
if (timer) clearTimer(timer);
|
|
@@ -452,6 +602,22 @@ export const START_COPY = {
|
|
|
452
602
|
headingStarting: 'Starting the server',
|
|
453
603
|
headingReady: 'Server is up',
|
|
454
604
|
headingFailed: 'The server did not start',
|
|
605
|
+
// A restart: each sentence is said only once the page holds what it says.
|
|
606
|
+
restartAsked: 'Asked the server to restart.',
|
|
607
|
+
restartStopping: 'The server accepted the restart and is shutting down.',
|
|
608
|
+
restartSlowStop: 'Still shutting down after {seconds} s.',
|
|
609
|
+
restartDown: 'The server stopped answering. Waiting for it to come back.',
|
|
610
|
+
restartRespawning: 'The supervisor is starting the server again.',
|
|
611
|
+
restartStalled: 'The server has not come back. Start it with the button, or run the command below.',
|
|
612
|
+
restartRefused: 'The server did not accept the restart: {reason}',
|
|
613
|
+
restartUnreachable: 'The server did not answer the restart request.',
|
|
614
|
+
restartStillRunning: 'The server accepted the restart but did not shut down.',
|
|
615
|
+
trailAccepted: 'Restart accepted',
|
|
616
|
+
trailNoAnswer: 'Server stopped answering',
|
|
617
|
+
trailStopped: 'Server process exited',
|
|
618
|
+
restartElapsedLabel: 'Time since the restart began',
|
|
619
|
+
headingRestarting: 'Restarting the server',
|
|
620
|
+
headingRestartFailed: 'The server did not restart',
|
|
455
621
|
};
|
|
456
622
|
|
|
457
623
|
/** `{name}` placeholders, like the app's t(). */
|
|
@@ -474,12 +640,15 @@ function startStepSeconds(ms) {
|
|
|
474
640
|
|
|
475
641
|
/**
|
|
476
642
|
* Everything a surface renders for one snapshot, as data:
|
|
643
|
+
* reason 'start' | 'restart' what is under way: a start, or a restart of a server that was up
|
|
477
644
|
* mascot 'asleep' | 'waking' | 'working' | 'ready' | 'failed' | 'stalled'
|
|
478
645
|
* busy the button is disabled (a start is under way)
|
|
479
|
-
* button 'start' | 'starting' | 'retry'
|
|
646
|
+
* button 'start' | 'starting' | 'retry' | 'none' (a restart: nothing to press) | 'back' (it was refused)
|
|
647
|
+
* heading key | null the title when it is not the surface's own for this mascot
|
|
480
648
|
* headline { key, params } | null the status sentence (the live region)
|
|
481
649
|
* hint { key, params } | null a second, quieter sentence
|
|
482
650
|
* elapsed '12 s' | '' the page's clock since the click
|
|
651
|
+
* elapsedLabel key what that clock counts from
|
|
483
652
|
* trail [{ key, time }] what was reported, with when
|
|
484
653
|
* error { message, log } | null
|
|
485
654
|
* A headline names a step only when the snapshot holds the report of it.
|
|
@@ -488,38 +657,57 @@ export function startStatusView(snap, { platform = 'linux', limits = START_LIMIT
|
|
|
488
657
|
const state = snap?.state || 'idle';
|
|
489
658
|
const launchOf = snap?.launch || null;
|
|
490
659
|
const sup = launchOf?.supervisor || null;
|
|
660
|
+
// A restart this page asked for or was told of; or one it only reads the report of.
|
|
661
|
+
const restart = snap?.restart || null;
|
|
662
|
+
const restartReport = launchOf?.reason === 'restart';
|
|
491
663
|
const view = {
|
|
492
664
|
state,
|
|
665
|
+
reason: restart || restartReport ? 'restart' : 'start',
|
|
493
666
|
mascot: 'asleep',
|
|
494
|
-
busy: state === 'launching' || state === 'starting' || state === 'waiting',
|
|
667
|
+
busy: state === 'launching' || state === 'starting' || state === 'waiting' || state === 'restarting',
|
|
495
668
|
button: 'start',
|
|
669
|
+
heading: null,
|
|
496
670
|
headline: null,
|
|
497
671
|
hint: null,
|
|
498
672
|
elapsed: '',
|
|
673
|
+
elapsedLabel: restart || restartReport ? 'restartElapsedLabel' : 'elapsedLabel',
|
|
499
674
|
trail: [],
|
|
500
675
|
error: null,
|
|
501
676
|
};
|
|
502
677
|
if (view.busy) {
|
|
503
|
-
view.button = 'starting';
|
|
678
|
+
view.button = restart ? 'none' : 'starting';
|
|
504
679
|
view.elapsed = formatStartElapsed(snap.elapsedMs);
|
|
680
|
+
if (view.reason === 'restart') view.heading = 'headingRestarting';
|
|
505
681
|
}
|
|
506
682
|
|
|
507
683
|
// The reported steps, each at the time it was reported for, counted from the
|
|
508
684
|
// click (or from the launch's own start when another page asked for it).
|
|
509
|
-
|
|
685
|
+
const firstPhase = (phase) => (sup ? sup.phases.find(p => p.phase === phase)?.at || (sup.phase === phase ? sup.at : 0) : 0);
|
|
686
|
+
if (restart) {
|
|
687
|
+
// A restart this page follows from its beginning: what the server said to
|
|
688
|
+
// it, what its own probe saw, then what the supervisor reports.
|
|
689
|
+
const add = (key, at) => { if (at !== null && at !== undefined) view.trail.push({ key, time: startStepSeconds(at - restart.askedAt) }); };
|
|
690
|
+
add('trailAccepted', restart.acceptedAt);
|
|
691
|
+
const exited = launchOf ? launchOf.steps.find(step => step.id === 'exited')?.at : undefined;
|
|
692
|
+
if (exited !== undefined) add('trailStopped', exited);
|
|
693
|
+
else add('trailNoAnswer', restart.downAt);
|
|
694
|
+
add('trailServer', firstPhase('server') || null);
|
|
695
|
+
add('trailListening', firstPhase('listening') || null);
|
|
696
|
+
} else if (launchOf) {
|
|
510
697
|
const asked = snap.askedAt !== null && snap.askedAt !== undefined && launchOf.startedAt >= snap.askedAt - 2000;
|
|
511
698
|
const zero = asked ? snap.askedAt : launchOf.startedAt;
|
|
512
699
|
const add = (key, at) => { if (at) view.trail.push({ key, time: startStepSeconds(at - zero) }); };
|
|
513
|
-
add('trailLauncher', launchOf.startedAt);
|
|
700
|
+
if (!restartReport) add('trailLauncher', launchOf.startedAt); // a restart has no launcher
|
|
514
701
|
for (const step of launchOf.steps) {
|
|
515
702
|
if (step.id === 'terminal') add('trailTerminal', step.at);
|
|
516
703
|
if (step.id === 'spawned') add('trailProcess', step.at);
|
|
704
|
+
if (step.id === 'exited') add('trailStopped', step.at);
|
|
517
705
|
}
|
|
518
706
|
if (sup) {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
add('trailServer',
|
|
522
|
-
add('trailListening',
|
|
707
|
+
// The supervisor of a restart was usually there long before it: listed only when it began with it.
|
|
708
|
+
if (!restartReport || sup.startedAt >= launchOf.startedAt - 2000) add('trailSupervisor', sup.startedAt);
|
|
709
|
+
add('trailServer', firstPhase('server'));
|
|
710
|
+
add('trailListening', firstPhase('listening'));
|
|
523
711
|
}
|
|
524
712
|
}
|
|
525
713
|
|
|
@@ -540,7 +728,34 @@ export function startStatusView(snap, { platform = 'linux', limits = START_LIMIT
|
|
|
540
728
|
}
|
|
541
729
|
if (state === 'stalled') {
|
|
542
730
|
view.mascot = 'stalled';
|
|
543
|
-
view.headline = launchOf?.state === 'gave-up' ? { key: 'gaveUp', params: {} }
|
|
731
|
+
view.headline = launchOf?.state === 'gave-up' ? { key: 'gaveUp', params: {} }
|
|
732
|
+
: restart ? { key: 'restartStalled', params: {} } : { key: 'stalled', params: {} };
|
|
733
|
+
return view;
|
|
734
|
+
}
|
|
735
|
+
if (state === 'refused') {
|
|
736
|
+
// The server is still there: nothing to start, only the way back to the app.
|
|
737
|
+
const why = restart?.refused || {};
|
|
738
|
+
view.mascot = 'failed';
|
|
739
|
+
view.button = 'back';
|
|
740
|
+
view.heading = 'headingRestartFailed';
|
|
741
|
+
view.headline = why.code === 'unreachable' ? { key: 'restartUnreachable', params: {} }
|
|
742
|
+
: why.code === 'still-running' ? { key: 'restartStillRunning', params: {} }
|
|
743
|
+
: { key: 'restartRefused', params: { reason: why.message || `HTTP ${why.status || 0}` } };
|
|
744
|
+
return view;
|
|
745
|
+
}
|
|
746
|
+
if (state === 'restarting' && restart) {
|
|
747
|
+
if (restart.downAt !== null) {
|
|
748
|
+
view.mascot = 'working';
|
|
749
|
+
view.headline = { key: 'restartDown', params: {} };
|
|
750
|
+
} else if (restart.acceptedAt !== null) {
|
|
751
|
+
view.mascot = 'working';
|
|
752
|
+
view.headline = { key: 'restartStopping', params: {} };
|
|
753
|
+
const stoppingMs = snap.elapsedMs - (restart.acceptedAt - restart.askedAt);
|
|
754
|
+
if (stoppingMs >= limits.quietAfterMs) view.hint = { key: 'restartSlowStop', params: { seconds: Math.floor(stoppingMs / 1000) } };
|
|
755
|
+
} else {
|
|
756
|
+
view.mascot = 'waking';
|
|
757
|
+
view.headline = { key: 'restartAsked', params: {} };
|
|
758
|
+
}
|
|
544
759
|
return view;
|
|
545
760
|
}
|
|
546
761
|
if (state === 'failed') {
|
|
@@ -568,6 +783,10 @@ export function startStatusView(snap, { platform = 'linux', limits = START_LIMIT
|
|
|
568
783
|
// Slow on purpose (an install, a download, a backup): silence there is expected.
|
|
569
784
|
const slow = ['dependencies', 'browser', 'build', 'snapshot'].includes(sup.phase);
|
|
570
785
|
if (isQuiet && !slow) view.hint = { key: 'quiet', params: { seconds: quiet } };
|
|
786
|
+
} else if (restartReport) {
|
|
787
|
+
// The supervisor opened this report to bring the server back; it has named no step yet.
|
|
788
|
+
view.headline = { key: 'restartRespawning', params: {} };
|
|
789
|
+
if (isQuiet) view.hint = { key: 'quiet', params: { seconds: quiet } };
|
|
571
790
|
} else if (launchOf.step === 'terminal') {
|
|
572
791
|
view.headline = isQuiet ? { key: 'terminalQuiet', params: { seconds: quiet } } : { key: 'terminalOpened', params: {} };
|
|
573
792
|
} else if (launchOf.step === 'spawned') {
|