@code-yeongyu/senpi 2026.7.25 → 2026.7.26
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 +81 -0
- package/README.md +1 -1
- package/dist/beta/omo-local-update.d.ts +207 -0
- package/dist/beta/omo-local-update.d.ts.map +1 -0
- package/dist/beta/omo-local-update.js +708 -0
- package/dist/beta/omo-local-update.js.map +1 -0
- package/dist/cli/args.d.ts +4 -16
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -25
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/grok-neo-gate.d.ts +12 -0
- package/dist/cli/grok-neo-gate.d.ts.map +1 -0
- package/dist/cli/grok-neo-gate.js +18 -0
- package/dist/cli/grok-neo-gate.js.map +1 -0
- package/dist/cli-main.js +4 -0
- package/dist/cli-main.js.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +2 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +33 -8
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +22 -3
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +202 -58
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-providers.d.ts +2 -2
- package/dist/core/auth-providers.js +2 -2
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/extensions/builtin/bash-timeout/timeout.d.ts.map +1 -1
- package/dist/core/extensions/builtin/bash-timeout/timeout.js +3 -2
- package/dist/core/extensions/builtin/bash-timeout/timeout.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +30 -2
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.js +28 -0
- package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
- package/dist/core/extensions/builtin/config-reload/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/config-reload/index.js +20 -17
- package/dist/core/extensions/builtin/config-reload/index.js.map +1 -1
- package/dist/core/extensions/builtin/config-reload/log.d.ts +4 -1
- package/dist/core/extensions/builtin/config-reload/log.d.ts.map +1 -1
- package/dist/core/extensions/builtin/config-reload/log.js +3 -0
- package/dist/core/extensions/builtin/config-reload/log.js.map +1 -1
- package/dist/core/extensions/builtin/config-reload/routine-settings.d.ts +15 -0
- package/dist/core/extensions/builtin/config-reload/routine-settings.d.ts.map +1 -0
- package/dist/core/extensions/builtin/config-reload/routine-settings.js +102 -0
- package/dist/core/extensions/builtin/config-reload/routine-settings.js.map +1 -0
- package/dist/core/extensions/builtin/goal/command-registration.js +2 -2
- package/dist/core/extensions/builtin/goal/command-registration.js.map +1 -1
- package/dist/core/extensions/builtin/goal/format.d.ts +1 -1
- package/dist/core/extensions/builtin/goal/format.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/format.js +9 -2
- package/dist/core/extensions/builtin/goal/format.js.map +1 -1
- package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/index.js +38 -23
- package/dist/core/extensions/builtin/goal/index.js.map +1 -1
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +6 -0
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts.map +1 -0
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +19 -0
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js.map +1 -0
- package/dist/core/extensions/builtin/goal/persistence.d.ts +6 -0
- package/dist/core/extensions/builtin/goal/persistence.d.ts.map +1 -0
- package/dist/core/extensions/builtin/goal/persistence.js +141 -0
- package/dist/core/extensions/builtin/goal/persistence.js.map +1 -0
- package/dist/core/extensions/builtin/goal/store.d.ts +7 -3
- package/dist/core/extensions/builtin/goal/store.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/store.js +66 -181
- package/dist/core/extensions/builtin/goal/store.js.map +1 -1
- package/dist/core/extensions/builtin/goal/tool-registration.d.ts +1 -0
- package/dist/core/extensions/builtin/goal/tool-registration.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/tool-registration.js +26 -15
- package/dist/core/extensions/builtin/goal/tool-registration.js.map +1 -1
- package/dist/core/extensions/builtin/goal/transitions.d.ts +3 -0
- package/dist/core/extensions/builtin/goal/transitions.d.ts.map +1 -0
- package/dist/core/extensions/builtin/goal/transitions.js +45 -0
- package/dist/core/extensions/builtin/goal/transitions.js.map +1 -0
- package/dist/core/extensions/builtin/goal/types.d.ts +10 -4
- package/dist/core/extensions/builtin/goal/types.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/types.js +2 -2
- package/dist/core/extensions/builtin/goal/types.js.map +1 -1
- package/dist/core/extensions/builtin/goal/ui.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/ui.js +2 -0
- package/dist/core/extensions/builtin/goal/ui.js.map +1 -1
- package/dist/core/extensions/builtin/goal/validation.d.ts +8 -1
- package/dist/core/extensions/builtin/goal/validation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/validation.js +24 -12
- package/dist/core/extensions/builtin/goal/validation.js.map +1 -1
- package/dist/core/extensions/builtin/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/index.js +0 -2
- package/dist/core/extensions/builtin/index.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/index.js +8 -5
- package/dist/core/extensions/builtin/mcp/index.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js +1 -0
- package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts +48 -0
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +88 -64
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +32 -0
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/index.d.ts +1 -1
- package/dist/core/extensions/builtin/terminal/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/index.js +1 -1
- package/dist/core/extensions/builtin/terminal/index.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-notify.d.ts +29 -0
- package/dist/core/extensions/builtin/terminal/monitor-notify.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-notify.js +185 -0
- package/dist/core/extensions/builtin/terminal/monitor-notify.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +35 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +98 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/notify.d.ts +9 -0
- package/dist/core/extensions/builtin/terminal/notify.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/notify.js +30 -14
- package/dist/core/extensions/builtin/terminal/notify.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/prompt.d.ts +1 -1
- package/dist/core/extensions/builtin/terminal/prompt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/prompt.js +13 -5
- package/dist/core/extensions/builtin/terminal/prompt.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/runtime-session.d.ts +3 -12
- package/dist/core/extensions/builtin/terminal/runtime-session.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/runtime-session.js +4 -59
- package/dist/core/extensions/builtin/terminal/runtime-session.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/settings.d.ts +8 -0
- package/dist/core/extensions/builtin/terminal/settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/settings.js +16 -2
- package/dist/core/extensions/builtin/terminal/settings.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/shared.d.ts +2 -2
- package/dist/core/extensions/builtin/terminal/shared.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/shared.js +2 -1
- package/dist/core/extensions/builtin/terminal/shared.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts +10 -4
- package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-output.js +17 -96
- package/dist/core/extensions/builtin/terminal/tools/bash-output.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts +7 -0
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +41 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor.js +97 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/render.d.ts +3 -0
- package/dist/core/extensions/builtin/terminal/tools/render.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/render.js +5 -2
- package/dist/core/extensions/builtin/terminal/tools/render.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/fuzzy-match.d.ts +19 -0
- package/dist/core/extensions/builtin/todotools/fuzzy-match.d.ts.map +1 -0
- package/dist/core/extensions/builtin/todotools/fuzzy-match.js +70 -0
- package/dist/core/extensions/builtin/todotools/fuzzy-match.js.map +1 -0
- package/dist/core/extensions/builtin/todotools/normalize.d.ts +8 -0
- package/dist/core/extensions/builtin/todotools/normalize.d.ts.map +1 -0
- package/dist/core/extensions/builtin/todotools/normalize.js +174 -0
- package/dist/core/extensions/builtin/todotools/normalize.js.map +1 -0
- package/dist/core/extensions/builtin/todotools/prompt.d.ts +2 -2
- package/dist/core/extensions/builtin/todotools/prompt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/prompt.js +4 -2
- package/dist/core/extensions/builtin/todotools/prompt.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/state.d.ts +9 -8
- package/dist/core/extensions/builtin/todotools/state.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/state.js +99 -48
- package/dist/core/extensions/builtin/todotools/state.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/tools/todo.d.ts +1 -1
- package/dist/core/extensions/builtin/todotools/tools/todo.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/tools/todo.js +65 -38
- package/dist/core/extensions/builtin/todotools/tools/todo.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/index.js +2 -4
- package/dist/core/extensions/builtin/websearch/index.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/config.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/config.js +4 -1
- package/dist/core/extensions/builtin/websearch/websearch/config.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/native.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/native.js +2 -4
- package/dist/core/extensions/builtin/websearch/websearch/native.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/renderers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/renderers.js +20 -8
- package/dist/core/extensions/builtin/websearch/websearch/renderers.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/search.d.ts +6 -1
- package/dist/core/extensions/builtin/websearch/websearch/search.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/search.js +15 -11
- package/dist/core/extensions/builtin/websearch/websearch/search.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/tool.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/tool.js +5 -10
- package/dist/core/extensions/builtin/websearch/websearch/tool.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/types.d.ts +1 -0
- package/dist/core/extensions/builtin/websearch/websearch/types.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/types.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +12 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +12 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +25 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/keybindings.d.ts +0 -5
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +0 -2
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/messages.d.ts +32 -0
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +91 -0
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-resolver.d.ts +10 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +13 -3
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +4 -3
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +19 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts +9 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +13 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts +7 -0
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +13 -0
- package/dist/core/retry-fallback/controller.js.map +1 -1
- package/dist/core/retry-fallback/cooldown.d.ts.map +1 -1
- package/dist/core/retry-fallback/cooldown.js +5 -0
- package/dist/core/retry-fallback/cooldown.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +11 -35
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +12 -16
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +10 -13
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/thinking-levels.d.ts.map +1 -1
- package/dist/core/thinking-levels.js +9 -2
- package/dist/core/thinking-levels.js.map +1 -1
- package/dist/core/timings.d.ts +7 -2
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +11 -0
- package/dist/core/timings.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +6 -0
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/inspector-policy.d.ts +22 -0
- package/dist/inspector-policy.d.ts.map +1 -0
- package/dist/inspector-policy.js +84 -0
- package/dist/inspector-policy.js.map +1 -0
- package/dist/main.d.ts +6 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +20 -23
- package/dist/main.js.map +1 -1
- package/dist/modes/app-server/runtime.d.ts.map +1 -1
- package/dist/modes/app-server/runtime.js +1 -0
- package/dist/modes/app-server/runtime.js.map +1 -1
- package/dist/modes/app-server/threads/goal-handlers.js +4 -2
- package/dist/modes/app-server/threads/goal-handlers.js.map +1 -1
- package/dist/modes/app-server/threads/goal-wire.d.ts.map +1 -1
- package/dist/modes/app-server/threads/goal-wire.js +1 -0
- package/dist/modes/app-server/threads/goal-wire.js.map +1 -1
- package/dist/modes/app-server/threads/handlers.d.ts +1 -0
- package/dist/modes/app-server/threads/handlers.d.ts.map +1 -1
- package/dist/modes/app-server/threads/handlers.js +2 -0
- package/dist/modes/app-server/threads/handlers.js.map +1 -1
- package/dist/modes/app-server/threads/turn-runtime.d.ts +1 -0
- package/dist/modes/app-server/threads/turn-runtime.d.ts.map +1 -1
- package/dist/modes/app-server/threads/turn-runtime.js.map +1 -1
- package/dist/modes/app-server/threads/turns.d.ts.map +1 -1
- package/dist/modes/app-server/threads/turns.js +47 -3
- package/dist/modes/app-server/threads/turns.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts +3 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +9 -2
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/status-indicator.d.ts +1 -0
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
- package/dist/modes/interactive/components/status-indicator.js +29 -4
- package/dist/modes/interactive/components/status-indicator.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +5 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +43 -17
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/grok/chrome-tokens.d.ts +22 -0
- package/dist/modes/interactive/grok/chrome-tokens.d.ts.map +1 -0
- package/dist/modes/interactive/grok/chrome-tokens.js +27 -0
- package/dist/modes/interactive/grok/chrome-tokens.js.map +1 -0
- package/dist/modes/interactive/grok/chrome.d.ts +51 -0
- package/dist/modes/interactive/grok/chrome.d.ts.map +1 -0
- package/dist/modes/interactive/grok/chrome.js +105 -0
- package/dist/modes/interactive/grok/chrome.js.map +1 -0
- package/dist/modes/interactive/grok/footer.d.ts +14 -0
- package/dist/modes/interactive/grok/footer.d.ts.map +1 -0
- package/dist/modes/interactive/grok/footer.js +27 -0
- package/dist/modes/interactive/grok/footer.js.map +1 -0
- package/dist/modes/interactive/grok/input-card.d.ts +9 -0
- package/dist/modes/interactive/grok/input-card.d.ts.map +1 -0
- package/dist/modes/interactive/grok/input-card.js +30 -0
- package/dist/modes/interactive/grok/input-card.js.map +1 -0
- package/dist/modes/interactive/grok/palette.d.ts +145 -0
- package/dist/modes/interactive/grok/palette.d.ts.map +1 -0
- package/dist/modes/interactive/grok/palette.js +92 -0
- package/dist/modes/interactive/grok/palette.js.map +1 -0
- package/dist/modes/interactive/grok/tool-row.d.ts +17 -0
- package/dist/modes/interactive/grok/tool-row.d.ts.map +1 -0
- package/dist/modes/interactive/grok/tool-row.js +26 -0
- package/dist/modes/interactive/grok/tool-row.js.map +1 -0
- package/dist/modes/interactive/grok/welcome-card.d.ts +10 -0
- package/dist/modes/interactive/grok/welcome-card.d.ts.map +1 -0
- package/dist/modes/interactive/grok/welcome-card.js +30 -0
- package/dist/modes/interactive/grok/welcome-card.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +117 -54
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/grok-day.json +77 -0
- package/dist/modes/interactive/theme/grok-night.json +93 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +4 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts +3 -3
- package/dist/modes/rpc/connection-handler.js +6 -6
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/custom-capability.d.ts +6 -7
- package/dist/modes/rpc/custom-capability.d.ts.map +1 -1
- package/dist/modes/rpc/custom-capability.js +6 -7
- package/dist/modes/rpc/custom-capability.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +2 -2
- package/dist/modes/rpc/rpc-mode.js +5 -5
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +11 -0
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/senpi +2 -0
- package/docs/docs.json +4 -0
- package/docs/environment-variables.md +1 -0
- package/docs/extensions.md +2 -2
- package/docs/grok-neo.md +77 -0
- package/docs/keybindings.md +0 -1
- package/docs/settings.md +14 -3
- package/docs/terminal-tools.md +6 -3
- package/docs/usage.md +1 -1
- package/examples/extensions/message-renderer.ts +2 -2
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +47 -0
- package/node_modules/@code-yeongyu/senpi-codemode/README.md +39 -19
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +3 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/http-server.ts +6 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/output-bridge.ts +0 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/eval-notifier.ts +40 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +46 -66
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +5 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel-contract.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +19 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py +7 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +8 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +23 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +322 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +253 -18
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/interrupt-note.ts +58 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +28 -6
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/status-events.ts +20 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +65 -22
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +4 -41
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +6 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +4 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts +10 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js +38 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +5 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +210 -26
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js +16 -8
- package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +39 -20
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +9 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.js +4 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +12 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +19 -11
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +121 -23
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +67 -31
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +13 -8
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/radius.d.ts +3 -2
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/radius.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/radius.js +37 -35
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/radius.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +11 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models-store.d.ts +5 -0
- package/node_modules/@earendil-works/pi-ai/dist/models-store.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models-store.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.js +5 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/anthropic.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +9 -0
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js +8 -3
- package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.d.ts +30 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.js +70 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/stop-details.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/stop-details.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/stop-details.js +7 -3
- package/node_modules/@earendil-works/pi-ai/dist/utils/stop-details.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/tool-call-id.d.ts +9 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/tool-call-id.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/tool-call-id.js +16 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/tool-call-id.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/package.json +1 -1
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js +35 -3
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +6 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts +32 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js +15 -7
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +8 -8
- package/dist/cli/neo/build-argv.d.ts +0 -19
- package/dist/cli/neo/build-argv.d.ts.map +0 -1
- package/dist/cli/neo/build-argv.js +0 -84
- package/dist/cli/neo/build-argv.js.map +0 -1
- package/dist/cli/neo/daemon-launch.d.ts +0 -25
- package/dist/cli/neo/daemon-launch.d.ts.map +0 -1
- package/dist/cli/neo/daemon-launch.js +0 -97
- package/dist/cli/neo/daemon-launch.js.map +0 -1
- package/dist/cli/neo/gate.d.ts +0 -15
- package/dist/cli/neo/gate.d.ts.map +0 -1
- package/dist/cli/neo/gate.js +0 -21
- package/dist/cli/neo/gate.js.map +0 -1
- package/dist/cli/neo/launch.d.ts +0 -24
- package/dist/cli/neo/launch.d.ts.map +0 -1
- package/dist/cli/neo/launch.js +0 -66
- package/dist/cli/neo/launch.js.map +0 -1
- package/dist/cli/neo/platform.d.ts +0 -38
- package/dist/cli/neo/platform.d.ts.map +0 -1
- package/dist/cli/neo/platform.js +0 -54
- package/dist/cli/neo/platform.js.map +0 -1
- package/dist/cli/neo/resolve-binary.d.ts +0 -39
- package/dist/cli/neo/resolve-binary.d.ts.map +0 -1
- package/dist/cli/neo/resolve-binary.js +0 -66
- package/dist/cli/neo/resolve-binary.js.map +0 -1
- package/dist/core/extensions/builtin/session-observer/index.d.ts +0 -5
- package/dist/core/extensions/builtin/session-observer/index.d.ts.map +0 -1
- package/dist/core/extensions/builtin/session-observer/index.js +0 -36
- package/dist/core/extensions/builtin/session-observer/index.js.map +0 -1
- package/dist/core/extensions/builtin/session-observer/loader.d.ts +0 -3
- package/dist/core/extensions/builtin/session-observer/loader.d.ts.map +0 -1
- package/dist/core/extensions/builtin/session-observer/loader.js +0 -20
- package/dist/core/extensions/builtin/session-observer/loader.js.map +0 -1
- package/dist/core/extensions/builtin/session-observer/overlay-format.d.ts +0 -7
- package/dist/core/extensions/builtin/session-observer/overlay-format.d.ts.map +0 -1
- package/dist/core/extensions/builtin/session-observer/overlay-format.js +0 -30
- package/dist/core/extensions/builtin/session-observer/overlay-format.js.map +0 -1
- package/dist/core/extensions/builtin/session-observer/overlay.d.ts +0 -51
- package/dist/core/extensions/builtin/session-observer/overlay.d.ts.map +0 -1
- package/dist/core/extensions/builtin/session-observer/overlay.js +0 -229
- package/dist/core/extensions/builtin/session-observer/overlay.js.map +0 -1
- package/dist/core/extensions/builtin/session-observer/scanner.d.ts +0 -10
- package/dist/core/extensions/builtin/session-observer/scanner.d.ts.map +0 -1
- package/dist/core/extensions/builtin/session-observer/scanner.js +0 -142
- package/dist/core/extensions/builtin/session-observer/scanner.js.map +0 -1
- package/dist/core/extensions/builtin/session-observer/text.d.ts +0 -7
- package/dist/core/extensions/builtin/session-observer/text.d.ts.map +0 -1
- package/dist/core/extensions/builtin/session-observer/text.js +0 -37
- package/dist/core/extensions/builtin/session-observer/text.js.map +0 -1
- package/dist/core/extensions/builtin/session-observer/transcript-entries.d.ts +0 -7
- package/dist/core/extensions/builtin/session-observer/transcript-entries.d.ts.map +0 -1
- package/dist/core/extensions/builtin/session-observer/transcript-entries.js +0 -71
- package/dist/core/extensions/builtin/session-observer/transcript-entries.js.map +0 -1
- package/dist/core/extensions/builtin/session-observer/transcript-format.d.ts +0 -11
- package/dist/core/extensions/builtin/session-observer/transcript-format.d.ts.map +0 -1
- package/dist/core/extensions/builtin/session-observer/transcript-format.js +0 -65
- package/dist/core/extensions/builtin/session-observer/transcript-format.js.map +0 -1
- package/dist/core/extensions/builtin/session-observer/transcript.d.ts +0 -4
- package/dist/core/extensions/builtin/session-observer/transcript.d.ts.map +0 -1
- package/dist/core/extensions/builtin/session-observer/transcript.js +0 -81
- package/dist/core/extensions/builtin/session-observer/transcript.js.map +0 -1
- package/dist/core/extensions/builtin/session-observer/types.d.ts +0 -33
- package/dist/core/extensions/builtin/session-observer/types.d.ts.map +0 -1
- package/dist/core/extensions/builtin/session-observer/types.js +0 -2
- package/dist/core/extensions/builtin/session-observer/types.js.map +0 -1
- package/dist/modes/rpc/neo-daemon-child-worker.d.ts +0 -29
- package/dist/modes/rpc/neo-daemon-child-worker.d.ts.map +0 -1
- package/dist/modes/rpc/neo-daemon-child-worker.js +0 -95
- package/dist/modes/rpc/neo-daemon-child-worker.js.map +0 -1
- package/dist/modes/rpc/neo-daemon-mode.d.ts +0 -110
- package/dist/modes/rpc/neo-daemon-mode.d.ts.map +0 -1
- package/dist/modes/rpc/neo-daemon-mode.js +0 -267
- package/dist/modes/rpc/neo-daemon-mode.js.map +0 -1
- package/dist/modes/rpc/neo-daemon-protocol.d.ts +0 -50
- package/dist/modes/rpc/neo-daemon-protocol.d.ts.map +0 -1
- package/dist/modes/rpc/neo-daemon-protocol.js +0 -66
- package/dist/modes/rpc/neo-daemon-protocol.js.map +0 -1
- package/dist/modes/rpc/neo-daemon-registry.d.ts +0 -98
- package/dist/modes/rpc/neo-daemon-registry.d.ts.map +0 -1
- package/dist/modes/rpc/neo-daemon-registry.js +0 -209
- package/dist/modes/rpc/neo-daemon-registry.js.map +0 -1
- package/dist/modes/rpc/neo-runtime-options-argv.d.ts +0 -17
- package/dist/modes/rpc/neo-runtime-options-argv.d.ts.map +0 -1
- package/dist/modes/rpc/neo-runtime-options-argv.js +0 -79
- package/dist/modes/rpc/neo-runtime-options-argv.js.map +0 -1
- package/dist/modes/rpc/neo-runtime-options.d.ts +0 -84
- package/dist/modes/rpc/neo-runtime-options.d.ts.map +0 -1
- package/dist/modes/rpc/neo-runtime-options.js +0 -92
- package/dist/modes/rpc/neo-runtime-options.js.map +0 -1
- package/docs/neo.md +0 -111
- package/node_modules/@code-yeongyu/senpi-codemode/src/codemode/runtime.ts +0 -258
- package/node_modules/@code-yeongyu/senpi-codemode/src/codemode/tools.ts +0 -106
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Neo daemon mode: a JSONL-over-socket server that serves N neo clients sharing
|
|
3
|
-
* one cwd, each with its own runtime.
|
|
4
|
-
*
|
|
5
|
-
* ISOLATION MODEL (see task-15 spike, evidence/task-15-neo-go-tui.md §1):
|
|
6
|
-
* running N `createAgentSessionRuntime` instances concurrently in a SINGLE node
|
|
7
|
-
* process is NOT safe — pi-ai's process-global provider registry
|
|
8
|
-
* (`resetApiProviders`) and pi-agent-core's module-level UUIDv7 counter cause
|
|
9
|
-
* cross-talk, and both are out of scope to change. The daemon therefore uses the
|
|
10
|
-
* plan's documented contingency: the daemon process is a SUPERVISOR that runs
|
|
11
|
-
* ONE worker (its own child process) per connection. Each worker is a standard,
|
|
12
|
-
* already-proven single-connection `runRpcMode` runtime with its own module
|
|
13
|
-
* state and its own AuthStorage, so a connection's `--api-key` can never leak
|
|
14
|
-
* into another connection. Concurrency is real (separate processes).
|
|
15
|
-
*
|
|
16
|
-
* The worker factory is injectable so tests can drive the supervisor
|
|
17
|
-
* deterministically (handshake, registry, idle) without spawning real children,
|
|
18
|
-
* while production spawns `node <cli> --mode rpc` per connection.
|
|
19
|
-
*
|
|
20
|
-
* REGISTRY/RACE: the daemon binds the socket FIRST (bind is the mutex — the race
|
|
21
|
-
* loser gets EADDRINUSE and the client attaches to the winner), THEN atomically
|
|
22
|
-
* self-registers as the LAST listen step. Clients never write the registry.
|
|
23
|
-
*
|
|
24
|
-
* CONNECTION LIFECYCLE: a socket disconnect aborts that connection's in-flight
|
|
25
|
-
* turn and disposes its worker; sessions persist incrementally to disk so
|
|
26
|
-
* recovery is resume-from-file. Idle shutdown fires after a configurable
|
|
27
|
-
* no-connection period.
|
|
28
|
-
*/
|
|
29
|
-
import { createServer } from "node:net";
|
|
30
|
-
import { attachJsonlLineReader, serializeJsonLine } from "./jsonl.js";
|
|
31
|
-
import { parseHello, validateHello, } from "./neo-daemon-protocol.js";
|
|
32
|
-
import { cleanupStaleNeoDaemon, NEO_DAEMON_PROTOCOL_VERSION, reassertNeoDaemonRecord, reclaimDeadSocketFile, removeNeoDaemonRecord, writeNeoDaemonRecord, } from "./neo-daemon-registry.js";
|
|
33
|
-
const realClock = {
|
|
34
|
-
setTimeout: (handler, ms) => setTimeout(handler, ms),
|
|
35
|
-
clearTimeout: (handle) => clearTimeout(handle),
|
|
36
|
-
};
|
|
37
|
-
/** Raised when the socket path is already bound (a daemon already won the race). */
|
|
38
|
-
export class NeoDaemonAddressInUseError extends Error {
|
|
39
|
-
constructor(listenPath) {
|
|
40
|
-
super(`Neo daemon socket already in use: ${listenPath}`);
|
|
41
|
-
this.name = "NeoDaemonAddressInUseError";
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Start the neo daemon. Binds the socket (throws {@link NeoDaemonAddressInUseError}
|
|
46
|
-
* if the race is lost), registers, and serves connections until idle-shutdown or
|
|
47
|
-
* an explicit `shutdown()`.
|
|
48
|
-
*/
|
|
49
|
-
export async function runNeoDaemon(options) {
|
|
50
|
-
const version = options.version ?? NEO_DAEMON_PROTOCOL_VERSION;
|
|
51
|
-
const register = options.register ?? true;
|
|
52
|
-
const clock = options.clock ?? realClock;
|
|
53
|
-
const selfHealIntervalMs = options.selfHealIntervalMs ?? 2000;
|
|
54
|
-
// The record this daemon owns. It is written once as the last listen step and
|
|
55
|
-
// re-asserted (self-heal) whenever it is found missing/corrupt — carrying this
|
|
56
|
-
// daemon's real in-memory token so the healed record still authenticates.
|
|
57
|
-
const ownRecord = {
|
|
58
|
-
version,
|
|
59
|
-
socket: options.listenPath,
|
|
60
|
-
pid: process.pid,
|
|
61
|
-
token: options.token,
|
|
62
|
-
};
|
|
63
|
-
// Stale cleanup BEFORE bind so a dead daemon's leftover socket/record does
|
|
64
|
-
// not block the fresh bind.
|
|
65
|
-
cleanupStaleNeoDaemon(options.agentDir, options.cwd);
|
|
66
|
-
const connections = new Set();
|
|
67
|
-
let idleTimer;
|
|
68
|
-
let selfHealTimer;
|
|
69
|
-
let shuttingDown = false;
|
|
70
|
-
let resolveClosed;
|
|
71
|
-
const closed = new Promise((resolve) => {
|
|
72
|
-
resolveClosed = resolve;
|
|
73
|
-
});
|
|
74
|
-
const server = createServer();
|
|
75
|
-
// Re-assert the registry record iff it is missing or no longer matches this
|
|
76
|
-
// daemon. No-op when register is false (a bind-race loser never owns the slot)
|
|
77
|
-
// or the daemon is shutting down (it is about to remove its record).
|
|
78
|
-
const selfHeal = () => {
|
|
79
|
-
if (!register || shuttingDown)
|
|
80
|
-
return;
|
|
81
|
-
reassertNeoDaemonRecord(options.agentDir, options.cwd, ownRecord);
|
|
82
|
-
};
|
|
83
|
-
const clearSelfHealTimer = () => {
|
|
84
|
-
if (selfHealTimer !== undefined) {
|
|
85
|
-
clock.clearTimeout(selfHealTimer);
|
|
86
|
-
selfHealTimer = undefined;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
// The clock only exposes one-shot setTimeout, so the recurring self-heal
|
|
90
|
-
// re-arms itself after each fire until shutdown clears it.
|
|
91
|
-
const armSelfHealTimer = () => {
|
|
92
|
-
if (!register || selfHealIntervalMs <= 0 || shuttingDown)
|
|
93
|
-
return;
|
|
94
|
-
selfHealTimer = clock.setTimeout(() => {
|
|
95
|
-
selfHeal();
|
|
96
|
-
selfHealTimer = undefined;
|
|
97
|
-
armSelfHealTimer();
|
|
98
|
-
}, selfHealIntervalMs);
|
|
99
|
-
};
|
|
100
|
-
const clearIdleTimer = () => {
|
|
101
|
-
if (idleTimer !== undefined) {
|
|
102
|
-
clock.clearTimeout(idleTimer);
|
|
103
|
-
idleTimer = undefined;
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
const armIdleTimer = () => {
|
|
107
|
-
clearIdleTimer();
|
|
108
|
-
if (options.idleShutdownMs <= 0)
|
|
109
|
-
return;
|
|
110
|
-
if (connections.size > 0)
|
|
111
|
-
return;
|
|
112
|
-
idleTimer = clock.setTimeout(() => {
|
|
113
|
-
void shutdown();
|
|
114
|
-
}, options.idleShutdownMs);
|
|
115
|
-
};
|
|
116
|
-
async function shutdown() {
|
|
117
|
-
if (shuttingDown)
|
|
118
|
-
return closed;
|
|
119
|
-
shuttingDown = true;
|
|
120
|
-
clearIdleTimer();
|
|
121
|
-
clearSelfHealTimer();
|
|
122
|
-
// Dispose all live connections (aborts each in-flight turn).
|
|
123
|
-
const disposals = [...connections].map((c) => c.teardown());
|
|
124
|
-
await Promise.allSettled(disposals);
|
|
125
|
-
connections.clear();
|
|
126
|
-
if (register) {
|
|
127
|
-
removeNeoDaemonRecord(options.agentDir, options.cwd);
|
|
128
|
-
}
|
|
129
|
-
await new Promise((resolve) => server.close(() => resolve()));
|
|
130
|
-
resolveClosed();
|
|
131
|
-
return closed;
|
|
132
|
-
}
|
|
133
|
-
server.on("connection", (socket) => {
|
|
134
|
-
void handleConnection(socket);
|
|
135
|
-
});
|
|
136
|
-
async function handleConnection(socket) {
|
|
137
|
-
clearIdleTimer();
|
|
138
|
-
// Self-heal on accept: a fresh client that found a lost/corrupt record but a
|
|
139
|
-
// LIVE deterministic socket pokes this daemon; re-asserting the record here
|
|
140
|
-
// lets that client's next poll find it and attach (closes the recovery wedge
|
|
141
|
-
// even before the recurring interval fires).
|
|
142
|
-
selfHeal();
|
|
143
|
-
socket.setNoDelay(true);
|
|
144
|
-
const abort = new AbortController();
|
|
145
|
-
const state = {
|
|
146
|
-
socket,
|
|
147
|
-
worker: undefined,
|
|
148
|
-
async teardown() {
|
|
149
|
-
abort.abort();
|
|
150
|
-
detach();
|
|
151
|
-
try {
|
|
152
|
-
await this.worker?.dispose();
|
|
153
|
-
}
|
|
154
|
-
finally {
|
|
155
|
-
if (!socket.destroyed)
|
|
156
|
-
socket.destroy();
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
const writeToClient = (line) => {
|
|
161
|
-
if (!socket.destroyed)
|
|
162
|
-
socket.write(line);
|
|
163
|
-
};
|
|
164
|
-
const reply = (message) => {
|
|
165
|
-
writeToClient(serializeJsonLine(message));
|
|
166
|
-
};
|
|
167
|
-
let handshakeDone = false;
|
|
168
|
-
const onLine = (line) => {
|
|
169
|
-
if (handshakeDone) {
|
|
170
|
-
state.worker?.handleLine(line);
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
handshakeDone = true;
|
|
174
|
-
void completeHandshake(line);
|
|
175
|
-
};
|
|
176
|
-
const detach = attachJsonlLineReader(socket, onLine);
|
|
177
|
-
const finalizeDrop = () => {
|
|
178
|
-
if (!connections.has(state))
|
|
179
|
-
return;
|
|
180
|
-
connections.delete(state);
|
|
181
|
-
void state.teardown();
|
|
182
|
-
armIdleTimer();
|
|
183
|
-
};
|
|
184
|
-
socket.on("close", finalizeDrop);
|
|
185
|
-
socket.on("error", finalizeDrop);
|
|
186
|
-
async function completeHandshake(firstLine) {
|
|
187
|
-
const hello = parseHello(firstLine);
|
|
188
|
-
if (!hello) {
|
|
189
|
-
reply({ type: "refuse", code: "malformed_hello", reason: "First line was not a valid hello message" });
|
|
190
|
-
socket.end();
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
const refusal = validateHello(hello, { token: options.token, version });
|
|
194
|
-
if (refusal) {
|
|
195
|
-
reply(refusal);
|
|
196
|
-
socket.end();
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
const runtimeOptions = hello.runtimeOptions ?? {};
|
|
200
|
-
const optionError = options.validateRuntimeOptions?.(runtimeOptions);
|
|
201
|
-
if (optionError) {
|
|
202
|
-
reply({ type: "refuse", code: "unsupported_options", reason: optionError });
|
|
203
|
-
socket.end();
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
// Accepted: build the per-connection worker, then welcome.
|
|
207
|
-
try {
|
|
208
|
-
const worker = await options.workerFactory({
|
|
209
|
-
runtimeOptions,
|
|
210
|
-
capabilities: hello.capabilities ?? [],
|
|
211
|
-
cwd: options.cwd,
|
|
212
|
-
agentDir: options.agentDir,
|
|
213
|
-
writeToClient,
|
|
214
|
-
signal: abort.signal,
|
|
215
|
-
});
|
|
216
|
-
state.worker = worker;
|
|
217
|
-
connections.add(state);
|
|
218
|
-
reply({ type: "welcome", version, capabilities: hello.capabilities });
|
|
219
|
-
}
|
|
220
|
-
catch (error) {
|
|
221
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
222
|
-
reply({ type: "refuse", code: "unsupported_options", reason: `Failed to start runtime: ${message}` });
|
|
223
|
-
socket.end();
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// A deterministic socket path means a crashed daemon can leave a dead socket
|
|
228
|
-
// FILE behind with no registry record for cleanupStaleNeoDaemon to key off.
|
|
229
|
-
// Reclaim such a leftover before bind so we do not hit EADDRINUSE forever; a
|
|
230
|
-
// socket a LIVE daemon still serves is left intact so we lose the race to it.
|
|
231
|
-
await reclaimDeadSocketFile(options.listenPath);
|
|
232
|
-
// Bind FIRST (the mutex). Map EADDRINUSE to a typed error so the caller can
|
|
233
|
-
// attach to the winner.
|
|
234
|
-
await new Promise((resolve, reject) => {
|
|
235
|
-
const onError = (error) => {
|
|
236
|
-
server.off("listening", onListening);
|
|
237
|
-
if (error.code === "EADDRINUSE") {
|
|
238
|
-
reject(new NeoDaemonAddressInUseError(options.listenPath));
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
reject(error);
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
const onListening = () => {
|
|
245
|
-
server.off("error", onError);
|
|
246
|
-
resolve();
|
|
247
|
-
};
|
|
248
|
-
server.once("error", onError);
|
|
249
|
-
server.once("listening", onListening);
|
|
250
|
-
server.listen(options.listenPath);
|
|
251
|
-
});
|
|
252
|
-
// Registry self-registration is the LAST listen step.
|
|
253
|
-
if (register) {
|
|
254
|
-
writeNeoDaemonRecord(options.agentDir, options.cwd, ownRecord);
|
|
255
|
-
}
|
|
256
|
-
// No connections yet: arm the idle timer immediately. Also start the recurring
|
|
257
|
-
// self-heal so a lost/corrupt record repairs itself even with no new connection.
|
|
258
|
-
armIdleTimer();
|
|
259
|
-
armSelfHealTimer();
|
|
260
|
-
return {
|
|
261
|
-
listenPath: options.listenPath,
|
|
262
|
-
closed,
|
|
263
|
-
connectionCount: () => connections.size,
|
|
264
|
-
shutdown,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
//# sourceMappingURL=neo-daemon-mode.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"neo-daemon-mode.js","sourceRoot":"","sources":["../../../src/modes/rpc/neo-daemon-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Neo daemon mode: a JSONL-over-socket server that serves N neo clients sharing\n * one cwd, each with its own runtime.\n *\n * ISOLATION MODEL (see task-15 spike, evidence/task-15-neo-go-tui.md §1):\n * running N `createAgentSessionRuntime` instances concurrently in a SINGLE node\n * process is NOT safe — pi-ai's process-global provider registry\n * (`resetApiProviders`) and pi-agent-core's module-level UUIDv7 counter cause\n * cross-talk, and both are out of scope to change. The daemon therefore uses the\n * plan's documented contingency: the daemon process is a SUPERVISOR that runs\n * ONE worker (its own child process) per connection. Each worker is a standard,\n * already-proven single-connection `runRpcMode` runtime with its own module\n * state and its own AuthStorage, so a connection's `--api-key` can never leak\n * into another connection. Concurrency is real (separate processes).\n *\n * The worker factory is injectable so tests can drive the supervisor\n * deterministically (handshake, registry, idle) without spawning real children,\n * while production spawns `node <cli> --mode rpc` per connection.\n *\n * REGISTRY/RACE: the daemon binds the socket FIRST (bind is the mutex — the race\n * loser gets EADDRINUSE and the client attaches to the winner), THEN atomically\n * self-registers as the LAST listen step. Clients never write the registry.\n *\n * CONNECTION LIFECYCLE: a socket disconnect aborts that connection's in-flight\n * turn and disposes its worker; sessions persist incrementally to disk so\n * recovery is resume-from-file. Idle shutdown fires after a configurable\n * no-connection period.\n */\n\nimport { createServer, type Server, type Socket } from \"node:net\";\nimport { attachJsonlLineReader, serializeJsonLine } from \"./jsonl.ts\";\nimport {\n\ttype NeoDaemonHandshakeReply,\n\ttype NeoHelloMessage,\n\tparseHello,\n\tvalidateHello,\n} from \"./neo-daemon-protocol.ts\";\nimport {\n\tcleanupStaleNeoDaemon,\n\tNEO_DAEMON_PROTOCOL_VERSION,\n\ttype NeoDaemonRecord,\n\treassertNeoDaemonRecord,\n\treclaimDeadSocketFile,\n\tremoveNeoDaemonRecord,\n\twriteNeoDaemonRecord,\n} from \"./neo-daemon-registry.ts\";\nimport type { NeoRuntimeOptions } from \"./neo-runtime-options.ts\";\n\n/** A live worker serving exactly one connection. */\nexport interface NeoDaemonWorker {\n\t/** Feed one inbound JSONL line from the client to the worker. */\n\thandleLine(line: string): void;\n\t/** Called when the connection drops; abort the in-flight turn and dispose. */\n\tdispose(): Promise<void>;\n}\n\n/**\n * Build a worker for one accepted connection. `writeToClient` sends a\n * (LF-terminated) JSONL line back to the client. `runtimeOptions` are the\n * per-connection options from the handshake. `signal` aborts when the\n * connection drops or the daemon shuts down.\n */\nexport type NeoDaemonWorkerFactory = (args: {\n\treadonly runtimeOptions: NeoRuntimeOptions;\n\treadonly capabilities: readonly string[];\n\treadonly cwd: string;\n\treadonly agentDir: string;\n\treadonly writeToClient: (line: string) => void;\n\treadonly signal: AbortSignal;\n}) => Promise<NeoDaemonWorker>;\n\n/** Minimal injectable clock so idle-shutdown timing is testable. */\nexport interface NeoDaemonClock {\n\tsetTimeout(handler: () => void, ms: number): unknown;\n\tclearTimeout(handle: unknown): void;\n}\n\nconst realClock: NeoDaemonClock = {\n\tsetTimeout: (handler, ms) => setTimeout(handler, ms),\n\tclearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n};\n\nexport interface NeoDaemonOptions {\n\t/** Unix socket path (POSIX) or named-pipe path (Windows) to bind. */\n\treadonly listenPath: string;\n\t/** Absolute cwd this daemon serves. */\n\treadonly cwd: string;\n\t/** Agent dir (holds the registry). */\n\treadonly agentDir: string;\n\t/** Self-register into the registry as the last listen step. Default true. */\n\treadonly register?: boolean;\n\t/** Idle shutdown period in ms; 0 disables. Default 30 min (caller supplies). */\n\treadonly idleShutdownMs: number;\n\t/**\n\t * Registry self-heal interval in ms; 0 disables the recurring re-assert (the\n\t * on-accept re-assert still runs). The daemon periodically re-writes its own\n\t * registry record if it went missing/corrupt, so a lost record repairs itself\n\t * even before the next connection. Default 2000ms (caller may override).\n\t */\n\treadonly selfHealIntervalMs?: number;\n\t/** Handshake token clients must present. */\n\treadonly token: string;\n\t/** Protocol version this daemon speaks. Default NEO_DAEMON_PROTOCOL_VERSION. */\n\treadonly version?: number;\n\t/** Worker factory (injected in tests; production spawns a child process). */\n\treadonly workerFactory: NeoDaemonWorkerFactory;\n\t/** Injectable clock for idle-shutdown timing. */\n\treadonly clock?: NeoDaemonClock;\n\t/**\n\t * Validate connection runtimeOptions; return a human reason to refuse, or\n\t * undefined to accept. Lets the host reject options it cannot honor.\n\t */\n\treadonly validateRuntimeOptions?: (options: NeoRuntimeOptions) => string | undefined;\n}\n\nexport interface NeoDaemonHandle {\n\t/** The bound socket path. */\n\treadonly listenPath: string;\n\t/** Resolves when the daemon has fully shut down. */\n\treadonly closed: Promise<void>;\n\t/** Current live connection count (for tests/observability). */\n\tconnectionCount(): number;\n\t/** Trigger a graceful shutdown. */\n\tshutdown(): Promise<void>;\n}\n\n/** Raised when the socket path is already bound (a daemon already won the race). */\nexport class NeoDaemonAddressInUseError extends Error {\n\tconstructor(listenPath: string) {\n\t\tsuper(`Neo daemon socket already in use: ${listenPath}`);\n\t\tthis.name = \"NeoDaemonAddressInUseError\";\n\t}\n}\n\n/**\n * Start the neo daemon. Binds the socket (throws {@link NeoDaemonAddressInUseError}\n * if the race is lost), registers, and serves connections until idle-shutdown or\n * an explicit `shutdown()`.\n */\nexport async function runNeoDaemon(options: NeoDaemonOptions): Promise<NeoDaemonHandle> {\n\tconst version = options.version ?? NEO_DAEMON_PROTOCOL_VERSION;\n\tconst register = options.register ?? true;\n\tconst clock = options.clock ?? realClock;\n\tconst selfHealIntervalMs = options.selfHealIntervalMs ?? 2000;\n\n\t// The record this daemon owns. It is written once as the last listen step and\n\t// re-asserted (self-heal) whenever it is found missing/corrupt — carrying this\n\t// daemon's real in-memory token so the healed record still authenticates.\n\tconst ownRecord: NeoDaemonRecord = {\n\t\tversion,\n\t\tsocket: options.listenPath,\n\t\tpid: process.pid,\n\t\ttoken: options.token,\n\t};\n\n\t// Stale cleanup BEFORE bind so a dead daemon's leftover socket/record does\n\t// not block the fresh bind.\n\tcleanupStaleNeoDaemon(options.agentDir, options.cwd);\n\n\tconst connections = new Set<ConnectionState>();\n\tlet idleTimer: unknown;\n\tlet selfHealTimer: unknown;\n\tlet shuttingDown = false;\n\tlet resolveClosed!: () => void;\n\tconst closed = new Promise<void>((resolve) => {\n\t\tresolveClosed = resolve;\n\t});\n\n\tconst server: Server = createServer();\n\n\t// Re-assert the registry record iff it is missing or no longer matches this\n\t// daemon. No-op when register is false (a bind-race loser never owns the slot)\n\t// or the daemon is shutting down (it is about to remove its record).\n\tconst selfHeal = (): void => {\n\t\tif (!register || shuttingDown) return;\n\t\treassertNeoDaemonRecord(options.agentDir, options.cwd, ownRecord);\n\t};\n\n\tconst clearSelfHealTimer = (): void => {\n\t\tif (selfHealTimer !== undefined) {\n\t\t\tclock.clearTimeout(selfHealTimer);\n\t\t\tselfHealTimer = undefined;\n\t\t}\n\t};\n\n\t// The clock only exposes one-shot setTimeout, so the recurring self-heal\n\t// re-arms itself after each fire until shutdown clears it.\n\tconst armSelfHealTimer = (): void => {\n\t\tif (!register || selfHealIntervalMs <= 0 || shuttingDown) return;\n\t\tselfHealTimer = clock.setTimeout(() => {\n\t\t\tselfHeal();\n\t\t\tselfHealTimer = undefined;\n\t\t\tarmSelfHealTimer();\n\t\t}, selfHealIntervalMs);\n\t};\n\n\tconst clearIdleTimer = (): void => {\n\t\tif (idleTimer !== undefined) {\n\t\t\tclock.clearTimeout(idleTimer);\n\t\t\tidleTimer = undefined;\n\t\t}\n\t};\n\n\tconst armIdleTimer = (): void => {\n\t\tclearIdleTimer();\n\t\tif (options.idleShutdownMs <= 0) return;\n\t\tif (connections.size > 0) return;\n\t\tidleTimer = clock.setTimeout(() => {\n\t\t\tvoid shutdown();\n\t\t}, options.idleShutdownMs);\n\t};\n\n\tasync function shutdown(): Promise<void> {\n\t\tif (shuttingDown) return closed;\n\t\tshuttingDown = true;\n\t\tclearIdleTimer();\n\t\tclearSelfHealTimer();\n\t\t// Dispose all live connections (aborts each in-flight turn).\n\t\tconst disposals = [...connections].map((c) => c.teardown());\n\t\tawait Promise.allSettled(disposals);\n\t\tconnections.clear();\n\t\tif (register) {\n\t\t\tremoveNeoDaemonRecord(options.agentDir, options.cwd);\n\t\t}\n\t\tawait new Promise<void>((resolve) => server.close(() => resolve()));\n\t\tresolveClosed();\n\t\treturn closed;\n\t}\n\n\tserver.on(\"connection\", (socket: Socket) => {\n\t\tvoid handleConnection(socket);\n\t});\n\n\tasync function handleConnection(socket: Socket): Promise<void> {\n\t\tclearIdleTimer();\n\t\t// Self-heal on accept: a fresh client that found a lost/corrupt record but a\n\t\t// LIVE deterministic socket pokes this daemon; re-asserting the record here\n\t\t// lets that client's next poll find it and attach (closes the recovery wedge\n\t\t// even before the recurring interval fires).\n\t\tselfHeal();\n\t\tsocket.setNoDelay(true);\n\t\tconst abort = new AbortController();\n\t\tconst state: ConnectionState = {\n\t\t\tsocket,\n\t\t\tworker: undefined,\n\t\t\tasync teardown() {\n\t\t\t\tabort.abort();\n\t\t\t\tdetach();\n\t\t\t\ttry {\n\t\t\t\t\tawait this.worker?.dispose();\n\t\t\t\t} finally {\n\t\t\t\t\tif (!socket.destroyed) socket.destroy();\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\n\t\tconst writeToClient = (line: string): void => {\n\t\t\tif (!socket.destroyed) socket.write(line);\n\t\t};\n\n\t\tconst reply = (message: NeoDaemonHandshakeReply): void => {\n\t\t\twriteToClient(serializeJsonLine(message));\n\t\t};\n\n\t\tlet handshakeDone = false;\n\n\t\tconst onLine = (line: string): void => {\n\t\t\tif (handshakeDone) {\n\t\t\t\tstate.worker?.handleLine(line);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thandshakeDone = true;\n\t\t\tvoid completeHandshake(line);\n\t\t};\n\n\t\tconst detach = attachJsonlLineReader(socket, onLine);\n\n\t\tconst finalizeDrop = (): void => {\n\t\t\tif (!connections.has(state)) return;\n\t\t\tconnections.delete(state);\n\t\t\tvoid state.teardown();\n\t\t\tarmIdleTimer();\n\t\t};\n\t\tsocket.on(\"close\", finalizeDrop);\n\t\tsocket.on(\"error\", finalizeDrop);\n\n\t\tasync function completeHandshake(firstLine: string): Promise<void> {\n\t\t\tconst hello = parseHello(firstLine);\n\t\t\tif (!hello) {\n\t\t\t\treply({ type: \"refuse\", code: \"malformed_hello\", reason: \"First line was not a valid hello message\" });\n\t\t\t\tsocket.end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst refusal = validateHello(hello, { token: options.token, version });\n\t\t\tif (refusal) {\n\t\t\t\treply(refusal);\n\t\t\t\tsocket.end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst runtimeOptions = hello.runtimeOptions ?? {};\n\t\t\tconst optionError = options.validateRuntimeOptions?.(runtimeOptions);\n\t\t\tif (optionError) {\n\t\t\t\treply({ type: \"refuse\", code: \"unsupported_options\", reason: optionError });\n\t\t\t\tsocket.end();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Accepted: build the per-connection worker, then welcome.\n\t\t\ttry {\n\t\t\t\tconst worker = await options.workerFactory({\n\t\t\t\t\truntimeOptions,\n\t\t\t\t\tcapabilities: hello.capabilities ?? [],\n\t\t\t\t\tcwd: options.cwd,\n\t\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t\twriteToClient,\n\t\t\t\t\tsignal: abort.signal,\n\t\t\t\t});\n\t\t\t\tstate.worker = worker;\n\t\t\t\tconnections.add(state);\n\t\t\t\treply({ type: \"welcome\", version, capabilities: hello.capabilities });\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\treply({ type: \"refuse\", code: \"unsupported_options\", reason: `Failed to start runtime: ${message}` });\n\t\t\t\tsocket.end();\n\t\t\t}\n\t\t}\n\t}\n\n\t// A deterministic socket path means a crashed daemon can leave a dead socket\n\t// FILE behind with no registry record for cleanupStaleNeoDaemon to key off.\n\t// Reclaim such a leftover before bind so we do not hit EADDRINUSE forever; a\n\t// socket a LIVE daemon still serves is left intact so we lose the race to it.\n\tawait reclaimDeadSocketFile(options.listenPath);\n\n\t// Bind FIRST (the mutex). Map EADDRINUSE to a typed error so the caller can\n\t// attach to the winner.\n\tawait new Promise<void>((resolve, reject) => {\n\t\tconst onError = (error: NodeJS.ErrnoException): void => {\n\t\t\tserver.off(\"listening\", onListening);\n\t\t\tif (error.code === \"EADDRINUSE\") {\n\t\t\t\treject(new NeoDaemonAddressInUseError(options.listenPath));\n\t\t\t} else {\n\t\t\t\treject(error);\n\t\t\t}\n\t\t};\n\t\tconst onListening = (): void => {\n\t\t\tserver.off(\"error\", onError);\n\t\t\tresolve();\n\t\t};\n\t\tserver.once(\"error\", onError);\n\t\tserver.once(\"listening\", onListening);\n\t\tserver.listen(options.listenPath);\n\t});\n\n\t// Registry self-registration is the LAST listen step.\n\tif (register) {\n\t\twriteNeoDaemonRecord(options.agentDir, options.cwd, ownRecord);\n\t}\n\n\t// No connections yet: arm the idle timer immediately. Also start the recurring\n\t// self-heal so a lost/corrupt record repairs itself even with no new connection.\n\tarmIdleTimer();\n\tarmSelfHealTimer();\n\n\treturn {\n\t\tlistenPath: options.listenPath,\n\t\tclosed,\n\t\tconnectionCount: () => connections.size,\n\t\tshutdown,\n\t};\n}\n\ninterface ConnectionState {\n\tsocket: Socket;\n\tworker: NeoDaemonWorker | undefined;\n\tteardown(): Promise<void>;\n}\n\nexport type { NeoHelloMessage };\n"]}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Neo daemon handshake protocol.
|
|
3
|
-
*
|
|
4
|
-
* A connection begins with a single `hello` line from the client. The daemon
|
|
5
|
-
* replies with exactly one `welcome` or `refuse` line, then (on welcome) the
|
|
6
|
-
* connection carries the ordinary JSONL RPC protocol for that connection's
|
|
7
|
-
* runtime. The handshake is additive and lives entirely on the socket — it does
|
|
8
|
-
* not touch the existing stdio RPC command shapes.
|
|
9
|
-
*/
|
|
10
|
-
import type { NeoRuntimeOptions } from "./neo-runtime-options.ts";
|
|
11
|
-
/** First line a client sends on a fresh connection. */
|
|
12
|
-
export interface NeoHelloMessage {
|
|
13
|
-
readonly type: "hello";
|
|
14
|
-
/** Must match the daemon's registry token. */
|
|
15
|
-
readonly token: string;
|
|
16
|
-
/** Client's protocol version; must equal the daemon's. */
|
|
17
|
-
readonly version: number;
|
|
18
|
-
/** Optional client capability flags (e.g. custom_unsupported opt-in). */
|
|
19
|
-
readonly capabilities?: readonly string[];
|
|
20
|
-
/** Per-connection runtime options used to build THIS connection's runtime. */
|
|
21
|
-
readonly runtimeOptions?: NeoRuntimeOptions;
|
|
22
|
-
}
|
|
23
|
-
/** Accepted-handshake reply. */
|
|
24
|
-
export interface NeoWelcomeMessage {
|
|
25
|
-
readonly type: "welcome";
|
|
26
|
-
readonly version: number;
|
|
27
|
-
/** Capabilities the daemon acknowledges for this connection. */
|
|
28
|
-
readonly capabilities?: readonly string[];
|
|
29
|
-
}
|
|
30
|
-
/** Rejected-handshake reply. The client then falls back to isolated transport. */
|
|
31
|
-
export interface NeoRefuseMessage {
|
|
32
|
-
readonly type: "refuse";
|
|
33
|
-
readonly reason: string;
|
|
34
|
-
readonly code: NeoRefuseCode;
|
|
35
|
-
}
|
|
36
|
-
export type NeoRefuseCode = "bad_token" | "version_mismatch" | "unsupported_options" | "malformed_hello";
|
|
37
|
-
export type NeoDaemonHandshakeReply = NeoWelcomeMessage | NeoRefuseMessage;
|
|
38
|
-
/** Parse an inbound first line as a hello message, or return undefined. */
|
|
39
|
-
export declare function parseHello(line: string): NeoHelloMessage | undefined;
|
|
40
|
-
/**
|
|
41
|
-
* Validate a hello against the daemon's token/version. Returns a refuse reply
|
|
42
|
-
* on mismatch, or undefined when the handshake is acceptable. `runtimeOptions`
|
|
43
|
-
* validation (unsupported/incompatible flags) is delegated to the caller since
|
|
44
|
-
* it depends on which options the daemon can honor.
|
|
45
|
-
*/
|
|
46
|
-
export declare function validateHello(hello: NeoHelloMessage, expected: {
|
|
47
|
-
token: string;
|
|
48
|
-
version?: number;
|
|
49
|
-
}): NeoRefuseMessage | undefined;
|
|
50
|
-
//# sourceMappingURL=neo-daemon-protocol.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"neo-daemon-protocol.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/neo-daemon-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,8EAA8E;IAC9E,QAAQ,CAAC,cAAc,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,gCAAgC;AAChC,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;CAC7B;AAED,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,kBAAkB,GAAG,qBAAqB,GAAG,iBAAiB,CAAC;AAEzG,MAAM,MAAM,uBAAuB,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAE3E,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CA0BpE;AAMD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3C,gBAAgB,GAAG,SAAS,CAa9B"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Neo daemon handshake protocol.
|
|
3
|
-
*
|
|
4
|
-
* A connection begins with a single `hello` line from the client. The daemon
|
|
5
|
-
* replies with exactly one `welcome` or `refuse` line, then (on welcome) the
|
|
6
|
-
* connection carries the ordinary JSONL RPC protocol for that connection's
|
|
7
|
-
* runtime. The handshake is additive and lives entirely on the socket — it does
|
|
8
|
-
* not touch the existing stdio RPC command shapes.
|
|
9
|
-
*/
|
|
10
|
-
import { NEO_DAEMON_PROTOCOL_VERSION } from "./neo-daemon-registry.js";
|
|
11
|
-
/** Parse an inbound first line as a hello message, or return undefined. */
|
|
12
|
-
export function parseHello(line) {
|
|
13
|
-
let parsed;
|
|
14
|
-
try {
|
|
15
|
-
parsed = JSON.parse(line);
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
if (typeof parsed !== "object" || parsed === null)
|
|
21
|
-
return undefined;
|
|
22
|
-
const record = parsed;
|
|
23
|
-
if (record.type !== "hello")
|
|
24
|
-
return undefined;
|
|
25
|
-
if (typeof record.token !== "string")
|
|
26
|
-
return undefined;
|
|
27
|
-
if (typeof record.version !== "number")
|
|
28
|
-
return undefined;
|
|
29
|
-
if (record.capabilities !== undefined && !isStringArray(record.capabilities))
|
|
30
|
-
return undefined;
|
|
31
|
-
if (record.runtimeOptions !== undefined &&
|
|
32
|
-
(typeof record.runtimeOptions !== "object" || record.runtimeOptions === null)) {
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
return {
|
|
36
|
-
type: "hello",
|
|
37
|
-
token: record.token,
|
|
38
|
-
version: record.version,
|
|
39
|
-
capabilities: record.capabilities,
|
|
40
|
-
runtimeOptions: record.runtimeOptions,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function isStringArray(value) {
|
|
44
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Validate a hello against the daemon's token/version. Returns a refuse reply
|
|
48
|
-
* on mismatch, or undefined when the handshake is acceptable. `runtimeOptions`
|
|
49
|
-
* validation (unsupported/incompatible flags) is delegated to the caller since
|
|
50
|
-
* it depends on which options the daemon can honor.
|
|
51
|
-
*/
|
|
52
|
-
export function validateHello(hello, expected) {
|
|
53
|
-
const version = expected.version ?? NEO_DAEMON_PROTOCOL_VERSION;
|
|
54
|
-
if (hello.version !== version) {
|
|
55
|
-
return {
|
|
56
|
-
type: "refuse",
|
|
57
|
-
code: "version_mismatch",
|
|
58
|
-
reason: `Protocol version mismatch: daemon speaks ${version}, client sent ${hello.version}`,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
if (hello.token !== expected.token) {
|
|
62
|
-
return { type: "refuse", code: "bad_token", reason: "Invalid handshake token" };
|
|
63
|
-
}
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
//# sourceMappingURL=neo-daemon-protocol.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"neo-daemon-protocol.js","sourceRoot":"","sources":["../../../src/modes/rpc/neo-daemon-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAmCvE,2EAA2E;AAC3E,MAAM,UAAU,UAAU,CAAC,IAAY;IACtC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACpE,MAAM,MAAM,GAAG,MAAiC,CAAC;IACjD,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IAC9C,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACvD,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACzD,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/F,IACC,MAAM,CAAC,cAAc,KAAK,SAAS;QACnC,CAAC,OAAO,MAAM,CAAC,cAAc,KAAK,QAAQ,IAAI,MAAM,CAAC,cAAc,KAAK,IAAI,CAAC,EAC5E,CAAC;QACF,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO;QACN,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,YAAY,EAAE,MAAM,CAAC,YAAoC;QACzD,cAAc,EAAE,MAAM,CAAC,cAA+C;KACtE,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACpC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC;AAChF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC5B,KAAsB,EACtB,QAA6C;IAE7C,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,IAAI,2BAA2B,CAAC;IAChE,IAAI,KAAK,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;QAC/B,OAAO;YACN,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,kBAAkB;YACxB,MAAM,EAAE,4CAA4C,OAAO,iBAAiB,KAAK,CAAC,OAAO,EAAE;SAC3F,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK,EAAE,CAAC;QACpC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC;IACjF,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC","sourcesContent":["/**\n * Neo daemon handshake protocol.\n *\n * A connection begins with a single `hello` line from the client. The daemon\n * replies with exactly one `welcome` or `refuse` line, then (on welcome) the\n * connection carries the ordinary JSONL RPC protocol for that connection's\n * runtime. The handshake is additive and lives entirely on the socket — it does\n * not touch the existing stdio RPC command shapes.\n */\n\nimport { NEO_DAEMON_PROTOCOL_VERSION } from \"./neo-daemon-registry.ts\";\nimport type { NeoRuntimeOptions } from \"./neo-runtime-options.ts\";\n\n/** First line a client sends on a fresh connection. */\nexport interface NeoHelloMessage {\n\treadonly type: \"hello\";\n\t/** Must match the daemon's registry token. */\n\treadonly token: string;\n\t/** Client's protocol version; must equal the daemon's. */\n\treadonly version: number;\n\t/** Optional client capability flags (e.g. custom_unsupported opt-in). */\n\treadonly capabilities?: readonly string[];\n\t/** Per-connection runtime options used to build THIS connection's runtime. */\n\treadonly runtimeOptions?: NeoRuntimeOptions;\n}\n\n/** Accepted-handshake reply. */\nexport interface NeoWelcomeMessage {\n\treadonly type: \"welcome\";\n\treadonly version: number;\n\t/** Capabilities the daemon acknowledges for this connection. */\n\treadonly capabilities?: readonly string[];\n}\n\n/** Rejected-handshake reply. The client then falls back to isolated transport. */\nexport interface NeoRefuseMessage {\n\treadonly type: \"refuse\";\n\treadonly reason: string;\n\treadonly code: NeoRefuseCode;\n}\n\nexport type NeoRefuseCode = \"bad_token\" | \"version_mismatch\" | \"unsupported_options\" | \"malformed_hello\";\n\nexport type NeoDaemonHandshakeReply = NeoWelcomeMessage | NeoRefuseMessage;\n\n/** Parse an inbound first line as a hello message, or return undefined. */\nexport function parseHello(line: string): NeoHelloMessage | undefined {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(line);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null) return undefined;\n\tconst record = parsed as Record<string, unknown>;\n\tif (record.type !== \"hello\") return undefined;\n\tif (typeof record.token !== \"string\") return undefined;\n\tif (typeof record.version !== \"number\") return undefined;\n\tif (record.capabilities !== undefined && !isStringArray(record.capabilities)) return undefined;\n\tif (\n\t\trecord.runtimeOptions !== undefined &&\n\t\t(typeof record.runtimeOptions !== \"object\" || record.runtimeOptions === null)\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\ttype: \"hello\",\n\t\ttoken: record.token,\n\t\tversion: record.version,\n\t\tcapabilities: record.capabilities as string[] | undefined,\n\t\truntimeOptions: record.runtimeOptions as NeoRuntimeOptions | undefined,\n\t};\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n\treturn Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\n/**\n * Validate a hello against the daemon's token/version. Returns a refuse reply\n * on mismatch, or undefined when the handshake is acceptable. `runtimeOptions`\n * validation (unsupported/incompatible flags) is delegated to the caller since\n * it depends on which options the daemon can honor.\n */\nexport function validateHello(\n\thello: NeoHelloMessage,\n\texpected: { token: string; version?: number },\n): NeoRefuseMessage | undefined {\n\tconst version = expected.version ?? NEO_DAEMON_PROTOCOL_VERSION;\n\tif (hello.version !== version) {\n\t\treturn {\n\t\t\ttype: \"refuse\",\n\t\t\tcode: \"version_mismatch\",\n\t\t\treason: `Protocol version mismatch: daemon speaks ${version}, client sent ${hello.version}`,\n\t\t};\n\t}\n\tif (hello.token !== expected.token) {\n\t\treturn { type: \"refuse\", code: \"bad_token\", reason: \"Invalid handshake token\" };\n\t}\n\treturn undefined;\n}\n"]}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Neo daemon registry: the on-disk record clients use to find a running daemon
|
|
3
|
-
* for a given cwd.
|
|
4
|
-
*
|
|
5
|
-
* Layout: `<agentDir>/neo-daemon/<cwd-key>.json`, mode 0600, containing
|
|
6
|
-
* `{ version, socket, pid, token }`. The daemon is the ONLY writer — clients
|
|
7
|
-
* only read. Writes are atomic (temp file + rename) so a client never observes a
|
|
8
|
-
* half-written record.
|
|
9
|
-
*
|
|
10
|
-
* `cwd-key` uses the same safe-path scheme as the session manager
|
|
11
|
-
* (session-manager.ts getDefaultSessionDirPath) so one cwd maps to exactly one
|
|
12
|
-
* daemon record.
|
|
13
|
-
*/
|
|
14
|
-
/** Bump when the wire protocol or handshake changes incompatibly. */
|
|
15
|
-
export declare const NEO_DAEMON_PROTOCOL_VERSION = 1;
|
|
16
|
-
export interface NeoDaemonRecord {
|
|
17
|
-
/** Protocol version the daemon speaks; clients refuse on mismatch. */
|
|
18
|
-
readonly version: number;
|
|
19
|
-
/** Absolute unix socket path (or Windows named-pipe path) the daemon listens on. */
|
|
20
|
-
readonly socket: string;
|
|
21
|
-
/** Daemon process id, used for stale-record detection. */
|
|
22
|
-
readonly pid: number;
|
|
23
|
-
/** Handshake token; a client must present it in `hello`. */
|
|
24
|
-
readonly token: string;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Compute the safe registry-key for a cwd. Mirrors the session-manager
|
|
28
|
-
* safe-path scheme so both derive the same key from the same cwd.
|
|
29
|
-
*/
|
|
30
|
-
export declare function neoDaemonCwdKey(cwd: string): string;
|
|
31
|
-
/** Directory holding all neo daemon registry records. */
|
|
32
|
-
export declare function neoDaemonRegistryDir(agentDir: string): string;
|
|
33
|
-
/** Absolute path to the registry record for a cwd. */
|
|
34
|
-
export declare function neoDaemonRegistryPath(agentDir: string, cwd: string): string;
|
|
35
|
-
/**
|
|
36
|
-
* Read the registry record for a cwd. Returns undefined when the file is
|
|
37
|
-
* missing, unreadable, or malformed (all non-fatal — the caller then spawns).
|
|
38
|
-
*/
|
|
39
|
-
export declare function readNeoDaemonRecord(agentDir: string, cwd: string): NeoDaemonRecord | undefined;
|
|
40
|
-
/**
|
|
41
|
-
* Atomically write the registry record (temp file + rename, mode 0600). The
|
|
42
|
-
* daemon calls this as the LAST listen step, after the socket is bound and
|
|
43
|
-
* listening.
|
|
44
|
-
*/
|
|
45
|
-
export declare function writeNeoDaemonRecord(agentDir: string, cwd: string, record: NeoDaemonRecord): void;
|
|
46
|
-
/**
|
|
47
|
-
* Re-assert the daemon's own registry record iff it is missing or does not match
|
|
48
|
-
* this daemon (version/socket/pid/token). Returns true when a (re-)write happened.
|
|
49
|
-
*
|
|
50
|
-
* This is the self-heal that closes the "live daemon owns the deterministic
|
|
51
|
-
* socket, but its registry record was lost/corrupted (or a SIGKILL left it
|
|
52
|
-
* missing)" recovery wedge: a fresh client cannot bind a new daemon (the live one
|
|
53
|
-
* owns the socket) and, with no record, cannot find the live one either. The
|
|
54
|
-
* daemon calls this on every accepted connection and on a short recurring
|
|
55
|
-
* interval, so a lost record repairs itself inside the client's attach-poll
|
|
56
|
-
* window. It NEVER clobbers an already-correct record (idempotent, no write when
|
|
57
|
-
* the record already points at this daemon), and because there is exactly one
|
|
58
|
-
* daemon per cwd (the bind mutex), it can never fight another live daemon. The
|
|
59
|
-
* re-asserted record carries the daemon's real in-memory token, so token auth is
|
|
60
|
-
* preserved — the client authenticates with the healed token exactly as it would
|
|
61
|
-
* with the original.
|
|
62
|
-
*/
|
|
63
|
-
export declare function reassertNeoDaemonRecord(agentDir: string, cwd: string, record: NeoDaemonRecord): boolean;
|
|
64
|
-
/** Remove the registry record for a cwd (best-effort; missing is fine). */
|
|
65
|
-
export declare function removeNeoDaemonRecord(agentDir: string, cwd: string): void;
|
|
66
|
-
/**
|
|
67
|
-
* Whether a pid is alive. `process.kill(pid, 0)` throws ESRCH for a dead pid and
|
|
68
|
-
* EPERM for a live pid we cannot signal (which still means "alive").
|
|
69
|
-
*/
|
|
70
|
-
export declare function isPidAlive(pid: number): boolean;
|
|
71
|
-
/**
|
|
72
|
-
* Clean up a stale record + its socket file for a cwd BEFORE a fresh daemon
|
|
73
|
-
* binds. A record is stale when its pid is dead. Returns true when something was
|
|
74
|
-
* cleaned. The bound socket path is unlinked so the fresh bind does not hit a
|
|
75
|
-
* leftover socket file.
|
|
76
|
-
*/
|
|
77
|
-
export declare function cleanupStaleNeoDaemon(agentDir: string, cwd: string): boolean;
|
|
78
|
-
/**
|
|
79
|
-
* Reclaim a leftover unix-socket file at `listenPath` when NO live daemon is
|
|
80
|
-
* serving it, so a fresh bind() does not hit EADDRINUSE forever.
|
|
81
|
-
*
|
|
82
|
-
* This is the safety net that makes the DETERMINISTIC socket path safe: a daemon
|
|
83
|
-
* SIGKILLed without a clean shutdown leaves the socket file on disk with its
|
|
84
|
-
* registry record gone (so {@link cleanupStaleNeoDaemon}, which keys off the
|
|
85
|
-
* record, misses it). bind() on a leftover unix-socket file throws EADDRINUSE
|
|
86
|
-
* whether or not anyone is listening, so the fresh daemon must probe the path and
|
|
87
|
-
* unlink it when it is dead.
|
|
88
|
-
*
|
|
89
|
-
* A LIVE daemon serving the socket accepts a connection; we must NOT unlink that
|
|
90
|
-
* one — the caller then loses the bind race (EADDRINUSE) and attaches to the
|
|
91
|
-
* winner, which is the intended "bind is the mutex" outcome. We only unlink when
|
|
92
|
-
* connecting is refused/errors (a dead socket) or the path is a plain file.
|
|
93
|
-
*
|
|
94
|
-
* No-op on Windows named pipes (not filesystem paths; the OS reclaims a pipe when
|
|
95
|
-
* its owner dies) and when nothing exists at the path.
|
|
96
|
-
*/
|
|
97
|
-
export declare function reclaimDeadSocketFile(listenPath: string): Promise<void>;
|
|
98
|
-
//# sourceMappingURL=neo-daemon-registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"neo-daemon-registry.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/neo-daemon-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,qEAAqE;AACrE,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,MAAM,WAAW,eAAe;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,yDAAyD;AACzD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,sDAAsD;AACtD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3E;AAaD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAe9F;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAOjG;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAavG;AAED,2EAA2E;AAC3E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAOzE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAS/C;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAmB5E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU7E"}
|