@jacques-ai/server 0.0.7-alpha.1
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/dist/config/config.d.ts +34 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +32 -0
- package/dist/config/config.js.map +1 -0
- package/dist/connection/applescript.d.ts +46 -0
- package/dist/connection/applescript.d.ts.map +1 -0
- package/dist/connection/applescript.js +62 -0
- package/dist/connection/applescript.js.map +1 -0
- package/dist/connection/applescript.test.d.ts +5 -0
- package/dist/connection/applescript.test.d.ts.map +1 -0
- package/dist/connection/applescript.test.js +64 -0
- package/dist/connection/applescript.test.js.map +1 -0
- package/dist/connection/constants.d.ts +88 -0
- package/dist/connection/constants.d.ts.map +1 -0
- package/dist/connection/constants.js +110 -0
- package/dist/connection/constants.js.map +1 -0
- package/dist/connection/git-info.d.ts +30 -0
- package/dist/connection/git-info.d.ts.map +1 -0
- package/dist/connection/git-info.js +52 -0
- package/dist/connection/git-info.js.map +1 -0
- package/dist/connection/git-info.test.d.ts +5 -0
- package/dist/connection/git-info.test.d.ts.map +1 -0
- package/dist/connection/git-info.test.js +35 -0
- package/dist/connection/git-info.test.js.map +1 -0
- package/dist/connection/index.d.ts +19 -0
- package/dist/connection/index.d.ts.map +1 -0
- package/dist/connection/index.js +36 -0
- package/dist/connection/index.js.map +1 -0
- package/dist/connection/process-detection.d.ts +58 -0
- package/dist/connection/process-detection.d.ts.map +1 -0
- package/dist/connection/process-detection.js +239 -0
- package/dist/connection/process-detection.js.map +1 -0
- package/dist/connection/process-detection.test.d.ts +5 -0
- package/dist/connection/process-detection.test.d.ts.map +1 -0
- package/dist/connection/process-detection.test.js +43 -0
- package/dist/connection/process-detection.test.js.map +1 -0
- package/dist/connection/session-discovery.d.ts +55 -0
- package/dist/connection/session-discovery.d.ts.map +1 -0
- package/dist/connection/session-discovery.js +311 -0
- package/dist/connection/session-discovery.js.map +1 -0
- package/dist/connection/terminal-key.d.ts +126 -0
- package/dist/connection/terminal-key.d.ts.map +1 -0
- package/dist/connection/terminal-key.js +271 -0
- package/dist/connection/terminal-key.js.map +1 -0
- package/dist/connection/terminal-key.test.d.ts +5 -0
- package/dist/connection/terminal-key.test.d.ts.map +1 -0
- package/dist/connection/terminal-key.test.js +221 -0
- package/dist/connection/terminal-key.test.js.map +1 -0
- package/dist/connection/worktree.d.ts +114 -0
- package/dist/connection/worktree.d.ts.map +1 -0
- package/dist/connection/worktree.js +320 -0
- package/dist/connection/worktree.js.map +1 -0
- package/dist/connection/worktree.test.d.ts +5 -0
- package/dist/connection/worktree.test.d.ts.map +1 -0
- package/dist/connection/worktree.test.js +113 -0
- package/dist/connection/worktree.test.js.map +1 -0
- package/dist/focus-watcher.d.ts +51 -0
- package/dist/focus-watcher.d.ts.map +1 -0
- package/dist/focus-watcher.js +169 -0
- package/dist/focus-watcher.js.map +1 -0
- package/dist/handlers/event-handler.d.ts +93 -0
- package/dist/handlers/event-handler.d.ts.map +1 -0
- package/dist/handlers/event-handler.js +196 -0
- package/dist/handlers/event-handler.js.map +1 -0
- package/dist/handlers/event-handler.test.d.ts +5 -0
- package/dist/handlers/event-handler.test.d.ts.map +1 -0
- package/dist/handlers/event-handler.test.js +305 -0
- package/dist/handlers/event-handler.test.js.map +1 -0
- package/dist/handlers/session-handler.d.ts +23 -0
- package/dist/handlers/session-handler.d.ts.map +1 -0
- package/dist/handlers/session-handler.js +104 -0
- package/dist/handlers/session-handler.js.map +1 -0
- package/dist/handlers/session-handler.test.d.ts +5 -0
- package/dist/handlers/session-handler.test.d.ts.map +1 -0
- package/dist/handlers/session-handler.test.js +89 -0
- package/dist/handlers/session-handler.test.js.map +1 -0
- package/dist/handlers/settings-handler.d.ts +32 -0
- package/dist/handlers/settings-handler.d.ts.map +1 -0
- package/dist/handlers/settings-handler.js +127 -0
- package/dist/handlers/settings-handler.js.map +1 -0
- package/dist/handlers/settings-handler.test.d.ts +5 -0
- package/dist/handlers/settings-handler.test.d.ts.map +1 -0
- package/dist/handlers/settings-handler.test.js +105 -0
- package/dist/handlers/settings-handler.test.js.map +1 -0
- package/dist/handlers/window-handler.d.ts +30 -0
- package/dist/handlers/window-handler.d.ts.map +1 -0
- package/dist/handlers/window-handler.js +486 -0
- package/dist/handlers/window-handler.js.map +1 -0
- package/dist/handlers/window-handler.test.d.ts +8 -0
- package/dist/handlers/window-handler.test.d.ts.map +1 -0
- package/dist/handlers/window-handler.test.js +167 -0
- package/dist/handlers/window-handler.test.js.map +1 -0
- package/dist/handlers/worktree-handler.d.ts +28 -0
- package/dist/handlers/worktree-handler.d.ts.map +1 -0
- package/dist/handlers/worktree-handler.js +268 -0
- package/dist/handlers/worktree-handler.js.map +1 -0
- package/dist/handlers/worktree-handler.test.d.ts +8 -0
- package/dist/handlers/worktree-handler.test.d.ts.map +1 -0
- package/dist/handlers/worktree-handler.test.js +118 -0
- package/dist/handlers/worktree-handler.test.js.map +1 -0
- package/dist/handlers/ws-utils.d.ts +12 -0
- package/dist/handlers/ws-utils.d.ts.map +1 -0
- package/dist/handlers/ws-utils.js +16 -0
- package/dist/handlers/ws-utils.js.map +1 -0
- package/dist/http-api.d.ts +26 -0
- package/dist/http-api.d.ts.map +1 -0
- package/dist/http-api.js +148 -0
- package/dist/http-api.js.map +1 -0
- package/dist/logger.d.ts +42 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +147 -0
- package/dist/logger.js.map +1 -0
- package/dist/logging/logger-factory.d.ts +51 -0
- package/dist/logging/logger-factory.d.ts.map +1 -0
- package/dist/logging/logger-factory.js +59 -0
- package/dist/logging/logger-factory.js.map +1 -0
- package/dist/mcp/search-tool.d.ts +65 -0
- package/dist/mcp/search-tool.d.ts.map +1 -0
- package/dist/mcp/search-tool.js +176 -0
- package/dist/mcp/search-tool.js.map +1 -0
- package/dist/mcp/server.d.ts +9 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +152 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/process-scanner.d.ts +96 -0
- package/dist/process-scanner.d.ts.map +1 -0
- package/dist/process-scanner.js +194 -0
- package/dist/process-scanner.js.map +1 -0
- package/dist/routes/__tests__/archive-routes.test.d.ts +5 -0
- package/dist/routes/__tests__/archive-routes.test.d.ts.map +1 -0
- package/dist/routes/__tests__/archive-routes.test.js +158 -0
- package/dist/routes/__tests__/archive-routes.test.js.map +1 -0
- package/dist/routes/__tests__/config-routes.test.d.ts +5 -0
- package/dist/routes/__tests__/config-routes.test.d.ts.map +1 -0
- package/dist/routes/__tests__/config-routes.test.js +112 -0
- package/dist/routes/__tests__/config-routes.test.js.map +1 -0
- package/dist/routes/__tests__/http-utils.test.d.ts +5 -0
- package/dist/routes/__tests__/http-utils.test.d.ts.map +1 -0
- package/dist/routes/__tests__/http-utils.test.js +102 -0
- package/dist/routes/__tests__/http-utils.test.js.map +1 -0
- package/dist/routes/__tests__/notification-routes.test.d.ts +5 -0
- package/dist/routes/__tests__/notification-routes.test.d.ts.map +1 -0
- package/dist/routes/__tests__/notification-routes.test.js +91 -0
- package/dist/routes/__tests__/notification-routes.test.js.map +1 -0
- package/dist/routes/__tests__/project-routes.test.d.ts +5 -0
- package/dist/routes/__tests__/project-routes.test.d.ts.map +1 -0
- package/dist/routes/__tests__/project-routes.test.js +168 -0
- package/dist/routes/__tests__/project-routes.test.js.map +1 -0
- package/dist/routes/__tests__/session-routes.test.d.ts +5 -0
- package/dist/routes/__tests__/session-routes.test.d.ts.map +1 -0
- package/dist/routes/__tests__/session-routes.test.js +198 -0
- package/dist/routes/__tests__/session-routes.test.js.map +1 -0
- package/dist/routes/__tests__/source-routes.test.d.ts +5 -0
- package/dist/routes/__tests__/source-routes.test.d.ts.map +1 -0
- package/dist/routes/__tests__/source-routes.test.js +142 -0
- package/dist/routes/__tests__/source-routes.test.js.map +1 -0
- package/dist/routes/__tests__/sync-routes.test.d.ts +5 -0
- package/dist/routes/__tests__/sync-routes.test.d.ts.map +1 -0
- package/dist/routes/__tests__/sync-routes.test.js +77 -0
- package/dist/routes/__tests__/sync-routes.test.js.map +1 -0
- package/dist/routes/__tests__/test-helpers.d.ts +47 -0
- package/dist/routes/__tests__/test-helpers.d.ts.map +1 -0
- package/dist/routes/__tests__/test-helpers.js +97 -0
- package/dist/routes/__tests__/test-helpers.js.map +1 -0
- package/dist/routes/archive-routes.d.ts +15 -0
- package/dist/routes/archive-routes.d.ts.map +1 -0
- package/dist/routes/archive-routes.js +181 -0
- package/dist/routes/archive-routes.js.map +1 -0
- package/dist/routes/claude-routes.d.ts +9 -0
- package/dist/routes/claude-routes.d.ts.map +1 -0
- package/dist/routes/claude-routes.js +47 -0
- package/dist/routes/claude-routes.js.map +1 -0
- package/dist/routes/config-routes.d.ts +9 -0
- package/dist/routes/config-routes.d.ts.map +1 -0
- package/dist/routes/config-routes.js +56 -0
- package/dist/routes/config-routes.js.map +1 -0
- package/dist/routes/config-store.d.ts +41 -0
- package/dist/routes/config-store.d.ts.map +1 -0
- package/dist/routes/config-store.js +52 -0
- package/dist/routes/config-store.js.map +1 -0
- package/dist/routes/http-utils.d.ts +32 -0
- package/dist/routes/http-utils.d.ts.map +1 -0
- package/dist/routes/http-utils.js +123 -0
- package/dist/routes/http-utils.js.map +1 -0
- package/dist/routes/index.d.ts +19 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +17 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/notification-routes.d.ts +10 -0
- package/dist/routes/notification-routes.d.ts.map +1 -0
- package/dist/routes/notification-routes.js +64 -0
- package/dist/routes/notification-routes.js.map +1 -0
- package/dist/routes/project-routes.d.ts +22 -0
- package/dist/routes/project-routes.d.ts.map +1 -0
- package/dist/routes/project-routes.js +415 -0
- package/dist/routes/project-routes.js.map +1 -0
- package/dist/routes/session-routes.d.ts +18 -0
- package/dist/routes/session-routes.d.ts.map +1 -0
- package/dist/routes/session-routes.js +609 -0
- package/dist/routes/session-routes.js.map +1 -0
- package/dist/routes/source-routes.d.ts +12 -0
- package/dist/routes/source-routes.d.ts.map +1 -0
- package/dist/routes/source-routes.js +119 -0
- package/dist/routes/source-routes.js.map +1 -0
- package/dist/routes/static-routes.d.ts +12 -0
- package/dist/routes/static-routes.d.ts.map +1 -0
- package/dist/routes/static-routes.js +52 -0
- package/dist/routes/static-routes.js.map +1 -0
- package/dist/routes/sync-routes.d.ts +9 -0
- package/dist/routes/sync-routes.d.ts.map +1 -0
- package/dist/routes/sync-routes.js +78 -0
- package/dist/routes/sync-routes.js.map +1 -0
- package/dist/routes/tile-routes.d.ts +10 -0
- package/dist/routes/tile-routes.d.ts.map +1 -0
- package/dist/routes/tile-routes.js +108 -0
- package/dist/routes/tile-routes.js.map +1 -0
- package/dist/routes/types.d.ts +17 -0
- package/dist/routes/types.d.ts.map +1 -0
- package/dist/routes/types.js +5 -0
- package/dist/routes/types.js.map +1 -0
- package/dist/routes/usage-routes.d.ts +8 -0
- package/dist/routes/usage-routes.d.ts.map +1 -0
- package/dist/routes/usage-routes.js +18 -0
- package/dist/routes/usage-routes.js.map +1 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +173 -0
- package/dist/server.js.map +1 -0
- package/dist/services/branch-divergence-service.d.ts +48 -0
- package/dist/services/branch-divergence-service.d.ts.map +1 -0
- package/dist/services/branch-divergence-service.js +156 -0
- package/dist/services/branch-divergence-service.js.map +1 -0
- package/dist/services/broadcast-service.d.ts +68 -0
- package/dist/services/broadcast-service.d.ts.map +1 -0
- package/dist/services/broadcast-service.js +78 -0
- package/dist/services/broadcast-service.js.map +1 -0
- package/dist/services/broadcast-service.test.d.ts +5 -0
- package/dist/services/broadcast-service.test.d.ts.map +1 -0
- package/dist/services/broadcast-service.test.js +130 -0
- package/dist/services/broadcast-service.test.js.map +1 -0
- package/dist/services/chat-service.d.ts +72 -0
- package/dist/services/chat-service.d.ts.map +1 -0
- package/dist/services/chat-service.js +342 -0
- package/dist/services/chat-service.js.map +1 -0
- package/dist/services/chat-system-prompt.d.ts +14 -0
- package/dist/services/chat-system-prompt.d.ts.map +1 -0
- package/dist/services/chat-system-prompt.js +68 -0
- package/dist/services/chat-system-prompt.js.map +1 -0
- package/dist/services/notification-service.d.ts +115 -0
- package/dist/services/notification-service.d.ts.map +1 -0
- package/dist/services/notification-service.js +424 -0
- package/dist/services/notification-service.js.map +1 -0
- package/dist/services/notification-service.test.d.ts +5 -0
- package/dist/services/notification-service.test.d.ts.map +1 -0
- package/dist/services/notification-service.test.js +918 -0
- package/dist/services/notification-service.test.js.map +1 -0
- package/dist/session/cleanup-service.d.ts +51 -0
- package/dist/session/cleanup-service.d.ts.map +1 -0
- package/dist/session/cleanup-service.js +98 -0
- package/dist/session/cleanup-service.js.map +1 -0
- package/dist/session/cleanup-service.test.d.ts +5 -0
- package/dist/session/cleanup-service.test.d.ts.map +1 -0
- package/dist/session/cleanup-service.test.js +121 -0
- package/dist/session/cleanup-service.test.js.map +1 -0
- package/dist/session/process-monitor.d.ts +79 -0
- package/dist/session/process-monitor.d.ts.map +1 -0
- package/dist/session/process-monitor.js +270 -0
- package/dist/session/process-monitor.js.map +1 -0
- package/dist/session/process-monitor.test.d.ts +5 -0
- package/dist/session/process-monitor.test.d.ts.map +1 -0
- package/dist/session/process-monitor.test.js +367 -0
- package/dist/session/process-monitor.test.js.map +1 -0
- package/dist/session/session-factory.d.ts +29 -0
- package/dist/session/session-factory.d.ts.map +1 -0
- package/dist/session/session-factory.js +123 -0
- package/dist/session/session-factory.js.map +1 -0
- package/dist/session/session-factory.test.d.ts +5 -0
- package/dist/session/session-factory.test.d.ts.map +1 -0
- package/dist/session/session-factory.test.js +299 -0
- package/dist/session/session-factory.test.js.map +1 -0
- package/dist/session-registry.d.ts +168 -0
- package/dist/session-registry.d.ts.map +1 -0
- package/dist/session-registry.js +626 -0
- package/dist/session-registry.js.map +1 -0
- package/dist/session-registry.test.d.ts +5 -0
- package/dist/session-registry.test.d.ts.map +1 -0
- package/dist/session-registry.test.js +582 -0
- package/dist/session-registry.test.js.map +1 -0
- package/dist/start-server.d.ts +31 -0
- package/dist/start-server.d.ts.map +1 -0
- package/dist/start-server.js +408 -0
- package/dist/start-server.js.map +1 -0
- package/dist/terminal-activator.d.ts +29 -0
- package/dist/terminal-activator.d.ts.map +1 -0
- package/dist/terminal-activator.js +264 -0
- package/dist/terminal-activator.js.map +1 -0
- package/dist/terminal-activator.test.d.ts +9 -0
- package/dist/terminal-activator.test.d.ts.map +1 -0
- package/dist/terminal-activator.test.js +95 -0
- package/dist/terminal-activator.test.js.map +1 -0
- package/dist/terminal-launcher.d.ts +51 -0
- package/dist/terminal-launcher.d.ts.map +1 -0
- package/dist/terminal-launcher.js +298 -0
- package/dist/terminal-launcher.js.map +1 -0
- package/dist/terminal-launcher.test.d.ts +8 -0
- package/dist/terminal-launcher.test.d.ts.map +1 -0
- package/dist/terminal-launcher.test.js +222 -0
- package/dist/terminal-launcher.test.js.map +1 -0
- package/dist/types.d.ts +796 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/dist/unix-socket.d.ts +68 -0
- package/dist/unix-socket.d.ts.map +1 -0
- package/dist/unix-socket.js +180 -0
- package/dist/unix-socket.js.map +1 -0
- package/dist/usage-limits.d.ts +13 -0
- package/dist/usage-limits.d.ts.map +1 -0
- package/dist/usage-limits.js +112 -0
- package/dist/usage-limits.js.map +1 -0
- package/dist/watchers/handoff-watcher.d.ts +74 -0
- package/dist/watchers/handoff-watcher.d.ts.map +1 -0
- package/dist/watchers/handoff-watcher.js +124 -0
- package/dist/watchers/handoff-watcher.js.map +1 -0
- package/dist/watchers/handoff-watcher.test.d.ts +8 -0
- package/dist/watchers/handoff-watcher.test.d.ts.map +1 -0
- package/dist/watchers/handoff-watcher.test.js +142 -0
- package/dist/watchers/handoff-watcher.test.js.map +1 -0
- package/dist/websocket.d.ts +107 -0
- package/dist/websocket.d.ts.map +1 -0
- package/dist/websocket.js +268 -0
- package/dist/websocket.js.map +1 -0
- package/dist/window-manager/index.d.ts +28 -0
- package/dist/window-manager/index.d.ts.map +1 -0
- package/dist/window-manager/index.js +56 -0
- package/dist/window-manager/index.js.map +1 -0
- package/dist/window-manager/layouts.d.ts +42 -0
- package/dist/window-manager/layouts.d.ts.map +1 -0
- package/dist/window-manager/layouts.js +133 -0
- package/dist/window-manager/layouts.js.map +1 -0
- package/dist/window-manager/linux-manager.d.ts +45 -0
- package/dist/window-manager/linux-manager.d.ts.map +1 -0
- package/dist/window-manager/linux-manager.js +299 -0
- package/dist/window-manager/linux-manager.js.map +1 -0
- package/dist/window-manager/macos-manager.d.ts +103 -0
- package/dist/window-manager/macos-manager.d.ts.map +1 -0
- package/dist/window-manager/macos-manager.js +637 -0
- package/dist/window-manager/macos-manager.js.map +1 -0
- package/dist/window-manager/smart-layouts.d.ts +116 -0
- package/dist/window-manager/smart-layouts.d.ts.map +1 -0
- package/dist/window-manager/smart-layouts.js +188 -0
- package/dist/window-manager/smart-layouts.js.map +1 -0
- package/dist/window-manager/smart-layouts.test.d.ts +8 -0
- package/dist/window-manager/smart-layouts.test.d.ts.map +1 -0
- package/dist/window-manager/smart-layouts.test.js +311 -0
- package/dist/window-manager/smart-layouts.test.js.map +1 -0
- package/dist/window-manager/tile-state.d.ts +87 -0
- package/dist/window-manager/tile-state.d.ts.map +1 -0
- package/dist/window-manager/tile-state.js +136 -0
- package/dist/window-manager/tile-state.js.map +1 -0
- package/dist/window-manager/tile-state.test.d.ts +8 -0
- package/dist/window-manager/tile-state.test.d.ts.map +1 -0
- package/dist/window-manager/tile-state.test.js +179 -0
- package/dist/window-manager/tile-state.test.js.map +1 -0
- package/dist/window-manager/types.d.ts +104 -0
- package/dist/window-manager/types.d.ts.map +1 -0
- package/dist/window-manager/types.js +8 -0
- package/dist/window-manager/types.js.map +1 -0
- package/dist/window-manager/windows-manager.d.ts +44 -0
- package/dist/window-manager/windows-manager.d.ts.map +1 -0
- package/dist/window-manager/windows-manager.js +281 -0
- package/dist/window-manager/windows-manager.js.map +1 -0
- package/dist/window-manager/windows-manager.test.d.ts +8 -0
- package/dist/window-manager/windows-manager.test.d.ts.map +1 -0
- package/dist/window-manager/windows-manager.test.js +183 -0
- package/dist/window-manager/windows-manager.test.js.map +1 -0
- package/gui-dist/assets/index-BmYIHRYe.js +142 -0
- package/gui-dist/assets/index-D_N5RH8O.css +1 -0
- package/gui-dist/assets/vendor-icons-ByXNrcwf.js +336 -0
- package/gui-dist/assets/vendor-markdown-DWPYwU1x.js +22 -0
- package/gui-dist/assets/vendor-react-CpILBTDM.js +59 -0
- package/gui-dist/index.html +17 -0
- package/gui-dist/jacsub.png +0 -0
- package/package.json +67 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process Monitor
|
|
3
|
+
*
|
|
4
|
+
* Handles process verification, bypass detection, and PID management.
|
|
5
|
+
* Extracted from SessionRegistry to separate process-level concerns
|
|
6
|
+
* from session state management.
|
|
7
|
+
*/
|
|
8
|
+
import { createLogger } from '../logging/logger-factory.js';
|
|
9
|
+
import { IDLE_TIMEOUT_MS, PROCESS_VERIFY_INTERVAL_MS, } from '../connection/constants.js';
|
|
10
|
+
import { extractPid } from '../connection/terminal-key.js';
|
|
11
|
+
import { isProcessRunning, isProcessBypass, getClaudeProcesses } from '../connection/process-detection.js';
|
|
12
|
+
/** Grace period for newly registered sessions before considering them dead (ms) */
|
|
13
|
+
const NEW_SESSION_GRACE_MS = 60_000; // 60 seconds
|
|
14
|
+
export class ProcessMonitor {
|
|
15
|
+
callbacks;
|
|
16
|
+
processVerifyInterval = null;
|
|
17
|
+
logger;
|
|
18
|
+
// CWDs where a session was launched with --dangerously-skip-permissions
|
|
19
|
+
pendingBypassCwds = new Set();
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.callbacks = options.callbacks;
|
|
22
|
+
this.logger = options.logger ?? createLogger({ silent: true });
|
|
23
|
+
}
|
|
24
|
+
get log() { return this.logger.log.bind(this.logger); }
|
|
25
|
+
get warn() { return this.logger.warn.bind(this.logger); }
|
|
26
|
+
/**
|
|
27
|
+
* Get PID for a session from any available source.
|
|
28
|
+
* Priority: terminal_key → session.terminal.terminal_pid
|
|
29
|
+
*/
|
|
30
|
+
getSessionPid(session) {
|
|
31
|
+
const keyPid = extractPid(session.terminal_key);
|
|
32
|
+
if (keyPid !== null) {
|
|
33
|
+
return keyPid;
|
|
34
|
+
}
|
|
35
|
+
if (session.terminal?.terminal_pid) {
|
|
36
|
+
return session.terminal.terminal_pid;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Store terminal_pid on a session if not already known.
|
|
42
|
+
* When a PID is first stored, checks if the process is running with
|
|
43
|
+
* --dangerously-skip-permissions and marks the session accordingly.
|
|
44
|
+
*/
|
|
45
|
+
storeTerminalPid(session, terminalPid) {
|
|
46
|
+
if (!terminalPid || terminalPid <= 0)
|
|
47
|
+
return;
|
|
48
|
+
const existingPid = this.getSessionPid(session);
|
|
49
|
+
if (existingPid === null) {
|
|
50
|
+
if (!session.terminal) {
|
|
51
|
+
session.terminal = {
|
|
52
|
+
tty: null,
|
|
53
|
+
terminal_pid: terminalPid,
|
|
54
|
+
term_program: null,
|
|
55
|
+
iterm_session_id: null,
|
|
56
|
+
term_session_id: null,
|
|
57
|
+
kitty_window_id: null,
|
|
58
|
+
wezterm_pane: null,
|
|
59
|
+
vscode_injection: null,
|
|
60
|
+
windowid: null,
|
|
61
|
+
term: null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
else if (!session.terminal.terminal_pid) {
|
|
65
|
+
session.terminal.terminal_pid = terminalPid;
|
|
66
|
+
}
|
|
67
|
+
this.log(`[Registry] Stored terminal_pid ${terminalPid} for session ${session.session_id}`);
|
|
68
|
+
}
|
|
69
|
+
// Now check if this process is bypass (async, non-blocking)
|
|
70
|
+
if (!session.is_bypass) {
|
|
71
|
+
isProcessBypass(terminalPid).then((bypass) => {
|
|
72
|
+
if (bypass) {
|
|
73
|
+
session.is_bypass = true;
|
|
74
|
+
this.log(`[Registry] Detected bypass from stored PID ${terminalPid} for session ${session.session_id}`);
|
|
75
|
+
}
|
|
76
|
+
}).catch(() => { });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Verify sessions are still valid:
|
|
81
|
+
* 1. Process is still running (checks all PID sources)
|
|
82
|
+
* 2. CWD is not in Trash
|
|
83
|
+
* 3. Session is not idle beyond timeout
|
|
84
|
+
* 4. PID-less sessions cross-referenced against running Claude processes
|
|
85
|
+
*/
|
|
86
|
+
async verifyProcesses() {
|
|
87
|
+
const sessionsToRemove = [];
|
|
88
|
+
const pidlessSessions = [];
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
for (const [id, session] of this.callbacks.getAllSessions()) {
|
|
91
|
+
// 1. Check if process is still running
|
|
92
|
+
const pid = this.getSessionPid(session);
|
|
93
|
+
if (pid !== null) {
|
|
94
|
+
const isRunning = await isProcessRunning(pid);
|
|
95
|
+
if (!isRunning) {
|
|
96
|
+
this.log(`[Registry] Process ${pid} no longer running for session ${id}`);
|
|
97
|
+
sessionsToRemove.push(id);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// Track PID-less sessions for batch process scan
|
|
103
|
+
pidlessSessions.push([id, session]);
|
|
104
|
+
}
|
|
105
|
+
// 2. Check if CWD is in Trash
|
|
106
|
+
if (session.cwd && session.cwd.includes('.Trash')) {
|
|
107
|
+
this.log(`[Registry] CWD in Trash for session ${id}: ${session.cwd}`);
|
|
108
|
+
sessionsToRemove.push(id);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// 3. Check for idle timeout
|
|
112
|
+
if (now - session.last_activity > IDLE_TIMEOUT_MS) {
|
|
113
|
+
const hoursIdle = ((now - session.last_activity) / (1000 * 60 * 60)).toFixed(1);
|
|
114
|
+
this.log(`[Registry] Session ${id} idle for ${hoursIdle}h, marking as stale`);
|
|
115
|
+
sessionsToRemove.push(id);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// 4. Process re-scan for PID-less sessions
|
|
120
|
+
if (pidlessSessions.length > 0) {
|
|
121
|
+
const removeSet = new Set(sessionsToRemove);
|
|
122
|
+
const remainingPidless = pidlessSessions.filter(([id]) => !removeSet.has(id));
|
|
123
|
+
if (remainingPidless.length > 0) {
|
|
124
|
+
await this.verifyPidlessSessions(remainingPidless, sessionsToRemove, now);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (const id of sessionsToRemove) {
|
|
128
|
+
this.log(`[Registry] Removing stale/dead session: ${id}`);
|
|
129
|
+
this.callbacks.removeSession(id);
|
|
130
|
+
}
|
|
131
|
+
if (sessionsToRemove.length > 0) {
|
|
132
|
+
this.log(`[Registry] Removed ${sessionsToRemove.length} stale/dead session(s)`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Verify PID-less sessions by scanning running Claude processes.
|
|
137
|
+
*
|
|
138
|
+
* For sessions without a PID, we can't use `kill -0`. Instead:
|
|
139
|
+
* 1. Enumerate all running Claude processes
|
|
140
|
+
* 2. Exclude PIDs already claimed by verified-alive sessions
|
|
141
|
+
* 3. Match PID-less sessions to unclaimed processes by CWD
|
|
142
|
+
* 4. If matched: enrich session with PID for future fast verification
|
|
143
|
+
* 5. If unmatched: session is dead, mark for removal
|
|
144
|
+
*/
|
|
145
|
+
async verifyPidlessSessions(pidlessSessions, sessionsToRemove, now) {
|
|
146
|
+
let processes;
|
|
147
|
+
try {
|
|
148
|
+
processes = await getClaudeProcesses();
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
// If process enumeration fails, don't remove anything (fail-safe)
|
|
152
|
+
this.warn(`[Registry] Failed to enumerate Claude processes for PID-less verification: ${err}`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Build set of PIDs already claimed by sessions WITH PIDs
|
|
156
|
+
const claimedPids = new Set();
|
|
157
|
+
for (const [, session] of this.callbacks.getAllSessions()) {
|
|
158
|
+
const pid = this.getSessionPid(session);
|
|
159
|
+
if (pid !== null) {
|
|
160
|
+
claimedPids.add(pid);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Build pool of unclaimed processes, indexed by normalized CWD
|
|
164
|
+
const unclaimedByCwd = new Map();
|
|
165
|
+
for (const proc of processes) {
|
|
166
|
+
if (claimedPids.has(proc.pid))
|
|
167
|
+
continue;
|
|
168
|
+
const normalizedCwd = proc.cwd.replace(/\/+$/, '');
|
|
169
|
+
const existing = unclaimedByCwd.get(normalizedCwd) || [];
|
|
170
|
+
existing.push(proc);
|
|
171
|
+
unclaimedByCwd.set(normalizedCwd, existing);
|
|
172
|
+
}
|
|
173
|
+
for (const [id, session] of pidlessSessions) {
|
|
174
|
+
// Grace period: skip sessions registered less than 60s ago
|
|
175
|
+
if (now - session.registered_at < NEW_SESSION_GRACE_MS) {
|
|
176
|
+
this.log(`[Registry] Skipping PID-less session ${id} (within grace period)`);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const sessionCwd = (session.cwd || '').replace(/\/+$/, '');
|
|
180
|
+
const candidates = unclaimedByCwd.get(sessionCwd);
|
|
181
|
+
if (candidates && candidates.length > 0) {
|
|
182
|
+
// Match found — claim the first unclaimed process and enrich with PID
|
|
183
|
+
const matched = candidates.shift();
|
|
184
|
+
if (candidates.length === 0) {
|
|
185
|
+
unclaimedByCwd.delete(sessionCwd);
|
|
186
|
+
}
|
|
187
|
+
this.storeTerminalPid(session, matched.pid);
|
|
188
|
+
claimedPids.add(matched.pid);
|
|
189
|
+
this.log(`[Registry] Enriched PID-less session ${id} with PID ${matched.pid} (matched by CWD: ${sessionCwd})`);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// No matching Claude process — session is dead
|
|
193
|
+
this.log(`[Registry] No matching Claude process for PID-less session ${id} (CWD: ${sessionCwd})`);
|
|
194
|
+
sessionsToRemove.push(id);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Detect bypass sessions by checking each session's actual process command line.
|
|
200
|
+
*/
|
|
201
|
+
async detectBypassSessions() {
|
|
202
|
+
const updated = [];
|
|
203
|
+
for (const [id, session] of this.callbacks.getAllSessions()) {
|
|
204
|
+
if (session.is_bypass)
|
|
205
|
+
continue;
|
|
206
|
+
const pid = this.getSessionPid(session);
|
|
207
|
+
if (pid === null)
|
|
208
|
+
continue;
|
|
209
|
+
const bypass = await isProcessBypass(pid);
|
|
210
|
+
if (bypass) {
|
|
211
|
+
session.is_bypass = true;
|
|
212
|
+
updated.push(id);
|
|
213
|
+
this.log(`[Registry] Detected bypass from PID ${pid} for session ${id}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return updated;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Start periodic process verification.
|
|
220
|
+
*/
|
|
221
|
+
startProcessVerification(intervalMs = PROCESS_VERIFY_INTERVAL_MS) {
|
|
222
|
+
if (this.processVerifyInterval) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// Run immediately on start
|
|
226
|
+
this.verifyProcesses().catch((err) => {
|
|
227
|
+
this.warn(`[Registry] Process verification failed: ${err}`);
|
|
228
|
+
});
|
|
229
|
+
// Then run periodically
|
|
230
|
+
this.processVerifyInterval = setInterval(() => {
|
|
231
|
+
this.verifyProcesses().catch((err) => {
|
|
232
|
+
this.warn(`[Registry] Process verification failed: ${err}`);
|
|
233
|
+
});
|
|
234
|
+
}, intervalMs);
|
|
235
|
+
this.log(`[Registry] Process verification started (interval: ${intervalMs / 1000}s)`);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Stop process verification interval.
|
|
239
|
+
*/
|
|
240
|
+
stopProcessVerification() {
|
|
241
|
+
if (this.processVerifyInterval) {
|
|
242
|
+
clearInterval(this.processVerifyInterval);
|
|
243
|
+
this.processVerifyInterval = null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Mark a CWD as expecting a bypass session.
|
|
248
|
+
* Auto-expires after 60s.
|
|
249
|
+
*/
|
|
250
|
+
markPendingBypass(cwd) {
|
|
251
|
+
const normalized = cwd.replace(/\/+$/, '');
|
|
252
|
+
this.pendingBypassCwds.add(normalized);
|
|
253
|
+
this.log(`[Registry] Marked pending bypass for: ${normalized}`);
|
|
254
|
+
setTimeout(() => {
|
|
255
|
+
this.pendingBypassCwds.delete(normalized);
|
|
256
|
+
}, 60_000);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Check if a CWD has a pending bypass flag, and consume it if so.
|
|
260
|
+
*/
|
|
261
|
+
consumePendingBypass(cwd) {
|
|
262
|
+
const normalized = cwd.replace(/\/+$/, '');
|
|
263
|
+
if (this.pendingBypassCwds.has(normalized)) {
|
|
264
|
+
this.pendingBypassCwds.delete(normalized);
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=process-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-monitor.js","sourceRoot":"","sources":["../../src/session/process-monitor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-monitor.test.d.ts","sourceRoot":"","sources":["../../src/session/process-monitor.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process Monitor Tests
|
|
3
|
+
*/
|
|
4
|
+
import { jest } from '@jest/globals';
|
|
5
|
+
// Mock process-detection module before importing the module under test
|
|
6
|
+
const mockIsProcessRunning = jest.fn();
|
|
7
|
+
const mockIsProcessBypass = jest.fn();
|
|
8
|
+
const mockGetClaudeProcesses = jest.fn();
|
|
9
|
+
jest.unstable_mockModule('../connection/process-detection.js', () => ({
|
|
10
|
+
isProcessRunning: mockIsProcessRunning,
|
|
11
|
+
isProcessBypass: mockIsProcessBypass,
|
|
12
|
+
getClaudeProcesses: mockGetClaudeProcesses,
|
|
13
|
+
}));
|
|
14
|
+
// Import after mocking
|
|
15
|
+
const { ProcessMonitor } = await import('./process-monitor.js');
|
|
16
|
+
import { createLogger } from '../logging/logger-factory.js';
|
|
17
|
+
function makeSession(overrides) {
|
|
18
|
+
return {
|
|
19
|
+
source: 'claude_code',
|
|
20
|
+
session_title: null,
|
|
21
|
+
transcript_path: null,
|
|
22
|
+
cwd: '/test',
|
|
23
|
+
project: 'test',
|
|
24
|
+
model: null,
|
|
25
|
+
workspace: null,
|
|
26
|
+
terminal: null,
|
|
27
|
+
terminal_key: 'TTY:/dev/ttys001',
|
|
28
|
+
status: 'active',
|
|
29
|
+
last_activity: Date.now(),
|
|
30
|
+
registered_at: Date.now(),
|
|
31
|
+
context_metrics: null,
|
|
32
|
+
autocompact: null,
|
|
33
|
+
last_tool_name: null,
|
|
34
|
+
...overrides,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
describe('ProcessMonitor', () => {
|
|
38
|
+
let monitor;
|
|
39
|
+
let sessions;
|
|
40
|
+
let removedSessionIds;
|
|
41
|
+
const silentLogger = createLogger({ silent: true });
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
sessions = new Map();
|
|
44
|
+
removedSessionIds = [];
|
|
45
|
+
jest.clearAllMocks();
|
|
46
|
+
mockGetClaudeProcesses.mockResolvedValue([]);
|
|
47
|
+
const callbacks = {
|
|
48
|
+
getSession: (id) => sessions.get(id),
|
|
49
|
+
getAllSessions: () => Array.from(sessions.entries()),
|
|
50
|
+
removeSession: (id) => {
|
|
51
|
+
sessions.delete(id);
|
|
52
|
+
removedSessionIds.push(id);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
monitor = new ProcessMonitor({ callbacks, logger: silentLogger });
|
|
56
|
+
});
|
|
57
|
+
describe('getSessionPid', () => {
|
|
58
|
+
it('should extract PID from DISCOVERED:PID: key', () => {
|
|
59
|
+
const session = makeSession({
|
|
60
|
+
session_id: 's1',
|
|
61
|
+
terminal_key: 'DISCOVERED:PID:12345',
|
|
62
|
+
});
|
|
63
|
+
expect(monitor.getSessionPid(session)).toBe(12345);
|
|
64
|
+
});
|
|
65
|
+
it('should extract PID from DISCOVERED:TTY: key', () => {
|
|
66
|
+
const session = makeSession({
|
|
67
|
+
session_id: 's1',
|
|
68
|
+
terminal_key: 'DISCOVERED:TTY:ttys001:67890',
|
|
69
|
+
});
|
|
70
|
+
expect(monitor.getSessionPid(session)).toBe(67890);
|
|
71
|
+
});
|
|
72
|
+
it('should fall back to terminal.terminal_pid', () => {
|
|
73
|
+
const session = makeSession({
|
|
74
|
+
session_id: 's1',
|
|
75
|
+
terminal_key: 'TTY:/dev/ttys001',
|
|
76
|
+
terminal: {
|
|
77
|
+
tty: '/dev/ttys001',
|
|
78
|
+
terminal_pid: 99999,
|
|
79
|
+
term_program: null,
|
|
80
|
+
iterm_session_id: null,
|
|
81
|
+
term_session_id: null,
|
|
82
|
+
kitty_window_id: null,
|
|
83
|
+
wezterm_pane: null,
|
|
84
|
+
vscode_injection: null,
|
|
85
|
+
windowid: null,
|
|
86
|
+
term: null,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
expect(monitor.getSessionPid(session)).toBe(99999);
|
|
90
|
+
});
|
|
91
|
+
it('should return null when no PID available', () => {
|
|
92
|
+
const session = makeSession({
|
|
93
|
+
session_id: 's1',
|
|
94
|
+
terminal_key: 'AUTO:some-uuid',
|
|
95
|
+
});
|
|
96
|
+
expect(monitor.getSessionPid(session)).toBeNull();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('storeTerminalPid', () => {
|
|
100
|
+
it('should create terminal object when none exists', () => {
|
|
101
|
+
mockIsProcessBypass.mockResolvedValue(false);
|
|
102
|
+
const session = makeSession({ session_id: 's1', terminal_key: 'AUTO:uuid' });
|
|
103
|
+
monitor.storeTerminalPid(session, 12345);
|
|
104
|
+
expect(session.terminal).not.toBeNull();
|
|
105
|
+
expect(session.terminal.terminal_pid).toBe(12345);
|
|
106
|
+
});
|
|
107
|
+
it('should set terminal_pid on existing terminal object', () => {
|
|
108
|
+
mockIsProcessBypass.mockResolvedValue(false);
|
|
109
|
+
const session = makeSession({
|
|
110
|
+
session_id: 's1',
|
|
111
|
+
terminal_key: 'AUTO:uuid',
|
|
112
|
+
terminal: {
|
|
113
|
+
tty: '/dev/ttys001',
|
|
114
|
+
terminal_pid: 0,
|
|
115
|
+
term_program: 'iTerm2',
|
|
116
|
+
iterm_session_id: null,
|
|
117
|
+
term_session_id: null,
|
|
118
|
+
kitty_window_id: null,
|
|
119
|
+
wezterm_pane: null,
|
|
120
|
+
vscode_injection: null,
|
|
121
|
+
windowid: null,
|
|
122
|
+
term: null,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
monitor.storeTerminalPid(session, 54321);
|
|
126
|
+
expect(session.terminal.terminal_pid).toBe(54321);
|
|
127
|
+
});
|
|
128
|
+
it('should not overwrite existing PID', () => {
|
|
129
|
+
mockIsProcessBypass.mockResolvedValue(false);
|
|
130
|
+
const session = makeSession({
|
|
131
|
+
session_id: 's1',
|
|
132
|
+
terminal_key: 'DISCOVERED:PID:11111',
|
|
133
|
+
});
|
|
134
|
+
monitor.storeTerminalPid(session, 22222);
|
|
135
|
+
// PID comes from terminal_key, should not create terminal object
|
|
136
|
+
expect(session.terminal).toBeNull();
|
|
137
|
+
});
|
|
138
|
+
it('should skip invalid PIDs', () => {
|
|
139
|
+
const session = makeSession({ session_id: 's1', terminal_key: 'AUTO:uuid' });
|
|
140
|
+
monitor.storeTerminalPid(session, 0);
|
|
141
|
+
monitor.storeTerminalPid(session, -1);
|
|
142
|
+
expect(session.terminal).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
describe('verifyProcesses', () => {
|
|
146
|
+
it('should remove sessions with dead processes', async () => {
|
|
147
|
+
mockIsProcessRunning.mockResolvedValue(false);
|
|
148
|
+
const session = makeSession({
|
|
149
|
+
session_id: 's1',
|
|
150
|
+
terminal_key: 'DISCOVERED:PID:12345',
|
|
151
|
+
});
|
|
152
|
+
sessions.set('s1', session);
|
|
153
|
+
await monitor.verifyProcesses();
|
|
154
|
+
expect(removedSessionIds).toContain('s1');
|
|
155
|
+
});
|
|
156
|
+
it('should keep sessions with running processes', async () => {
|
|
157
|
+
mockIsProcessRunning.mockResolvedValue(true);
|
|
158
|
+
const session = makeSession({
|
|
159
|
+
session_id: 's1',
|
|
160
|
+
terminal_key: 'DISCOVERED:PID:12345',
|
|
161
|
+
});
|
|
162
|
+
sessions.set('s1', session);
|
|
163
|
+
await monitor.verifyProcesses();
|
|
164
|
+
expect(removedSessionIds).toHaveLength(0);
|
|
165
|
+
});
|
|
166
|
+
it('should remove sessions with CWD in Trash', async () => {
|
|
167
|
+
const session = makeSession({
|
|
168
|
+
session_id: 's1',
|
|
169
|
+
terminal_key: 'AUTO:uuid',
|
|
170
|
+
cwd: '/Users/test/.Trash/old-project',
|
|
171
|
+
});
|
|
172
|
+
sessions.set('s1', session);
|
|
173
|
+
await monitor.verifyProcesses();
|
|
174
|
+
expect(removedSessionIds).toContain('s1');
|
|
175
|
+
});
|
|
176
|
+
it('should remove sessions idle beyond timeout', async () => {
|
|
177
|
+
const session = makeSession({
|
|
178
|
+
session_id: 's1',
|
|
179
|
+
terminal_key: 'AUTO:uuid',
|
|
180
|
+
last_activity: Date.now() - (5 * 60 * 60 * 1000), // 5 hours ago
|
|
181
|
+
});
|
|
182
|
+
sessions.set('s1', session);
|
|
183
|
+
await monitor.verifyProcesses();
|
|
184
|
+
expect(removedSessionIds).toContain('s1');
|
|
185
|
+
});
|
|
186
|
+
it('should not remove recently active sessions without PID', async () => {
|
|
187
|
+
const session = makeSession({
|
|
188
|
+
session_id: 's1',
|
|
189
|
+
terminal_key: 'AUTO:uuid',
|
|
190
|
+
last_activity: Date.now(),
|
|
191
|
+
});
|
|
192
|
+
sessions.set('s1', session);
|
|
193
|
+
await monitor.verifyProcesses();
|
|
194
|
+
expect(removedSessionIds).toHaveLength(0);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe('detectBypassSessions', () => {
|
|
198
|
+
it('should mark sessions as bypass when process has flag', async () => {
|
|
199
|
+
mockIsProcessBypass.mockResolvedValue(true);
|
|
200
|
+
const session = makeSession({
|
|
201
|
+
session_id: 's1',
|
|
202
|
+
terminal_key: 'DISCOVERED:PID:12345',
|
|
203
|
+
});
|
|
204
|
+
sessions.set('s1', session);
|
|
205
|
+
const updated = await monitor.detectBypassSessions();
|
|
206
|
+
expect(updated).toContain('s1');
|
|
207
|
+
expect(session.is_bypass).toBe(true);
|
|
208
|
+
});
|
|
209
|
+
it('should skip already-bypass sessions', async () => {
|
|
210
|
+
mockIsProcessBypass.mockResolvedValue(true);
|
|
211
|
+
const session = makeSession({
|
|
212
|
+
session_id: 's1',
|
|
213
|
+
terminal_key: 'DISCOVERED:PID:12345',
|
|
214
|
+
is_bypass: true,
|
|
215
|
+
});
|
|
216
|
+
sessions.set('s1', session);
|
|
217
|
+
const updated = await monitor.detectBypassSessions();
|
|
218
|
+
expect(updated).toHaveLength(0);
|
|
219
|
+
expect(mockIsProcessBypass).not.toHaveBeenCalled();
|
|
220
|
+
});
|
|
221
|
+
it('should skip sessions without PID', async () => {
|
|
222
|
+
const session = makeSession({
|
|
223
|
+
session_id: 's1',
|
|
224
|
+
terminal_key: 'AUTO:uuid',
|
|
225
|
+
});
|
|
226
|
+
sessions.set('s1', session);
|
|
227
|
+
const updated = await monitor.detectBypassSessions();
|
|
228
|
+
expect(updated).toHaveLength(0);
|
|
229
|
+
expect(mockIsProcessBypass).not.toHaveBeenCalled();
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
describe('verifyProcesses - PID-less session verification', () => {
|
|
233
|
+
it('should remove PID-less session when no Claude processes running', async () => {
|
|
234
|
+
mockGetClaudeProcesses.mockResolvedValue([]);
|
|
235
|
+
const session = makeSession({
|
|
236
|
+
session_id: 's1',
|
|
237
|
+
terminal_key: 'AUTO:some-uuid',
|
|
238
|
+
cwd: '/Users/test/project',
|
|
239
|
+
registered_at: Date.now() - 120_000, // 2 min ago, past grace period
|
|
240
|
+
});
|
|
241
|
+
sessions.set('s1', session);
|
|
242
|
+
await monitor.verifyProcesses();
|
|
243
|
+
expect(removedSessionIds).toContain('s1');
|
|
244
|
+
expect(mockGetClaudeProcesses).toHaveBeenCalledTimes(1);
|
|
245
|
+
});
|
|
246
|
+
it('should keep PID-less session when matching process found by CWD', async () => {
|
|
247
|
+
mockIsProcessBypass.mockResolvedValue(false);
|
|
248
|
+
mockGetClaudeProcesses.mockResolvedValue([
|
|
249
|
+
{ pid: 55555, tty: 'ttys001', cwd: '/Users/test/project' },
|
|
250
|
+
]);
|
|
251
|
+
const session = makeSession({
|
|
252
|
+
session_id: 's1',
|
|
253
|
+
terminal_key: 'AUTO:some-uuid',
|
|
254
|
+
cwd: '/Users/test/project',
|
|
255
|
+
registered_at: Date.now() - 120_000,
|
|
256
|
+
});
|
|
257
|
+
sessions.set('s1', session);
|
|
258
|
+
await monitor.verifyProcesses();
|
|
259
|
+
expect(removedSessionIds).toHaveLength(0);
|
|
260
|
+
// Verify PID was enriched
|
|
261
|
+
expect(monitor.getSessionPid(session)).toBe(55555);
|
|
262
|
+
});
|
|
263
|
+
it('should not remove PID-less session within grace period', async () => {
|
|
264
|
+
mockGetClaudeProcesses.mockResolvedValue([]); // No matching processes
|
|
265
|
+
const session = makeSession({
|
|
266
|
+
session_id: 's1',
|
|
267
|
+
terminal_key: 'AUTO:some-uuid',
|
|
268
|
+
cwd: '/Users/test/project',
|
|
269
|
+
registered_at: Date.now() - 10_000, // 10s ago, within grace period
|
|
270
|
+
});
|
|
271
|
+
sessions.set('s1', session);
|
|
272
|
+
await monitor.verifyProcesses();
|
|
273
|
+
expect(removedSessionIds).toHaveLength(0);
|
|
274
|
+
});
|
|
275
|
+
it('should not claim processes already owned by PID-bearing sessions', async () => {
|
|
276
|
+
mockIsProcessRunning.mockResolvedValue(true);
|
|
277
|
+
mockGetClaudeProcesses.mockResolvedValue([
|
|
278
|
+
{ pid: 11111, tty: 'ttys001', cwd: '/Users/test/project' },
|
|
279
|
+
]);
|
|
280
|
+
// Session with PID (already claims PID 11111)
|
|
281
|
+
const sessionWithPid = makeSession({
|
|
282
|
+
session_id: 's1',
|
|
283
|
+
terminal_key: 'DISCOVERED:PID:11111',
|
|
284
|
+
cwd: '/Users/test/project',
|
|
285
|
+
});
|
|
286
|
+
// PID-less session in same CWD
|
|
287
|
+
const sessionWithoutPid = makeSession({
|
|
288
|
+
session_id: 's2',
|
|
289
|
+
terminal_key: 'AUTO:uuid2',
|
|
290
|
+
cwd: '/Users/test/project',
|
|
291
|
+
registered_at: Date.now() - 120_000,
|
|
292
|
+
});
|
|
293
|
+
sessions.set('s1', sessionWithPid);
|
|
294
|
+
sessions.set('s2', sessionWithoutPid);
|
|
295
|
+
await monitor.verifyProcesses();
|
|
296
|
+
// s2 should be removed because the only process (PID 11111) is claimed by s1
|
|
297
|
+
expect(removedSessionIds).toContain('s2');
|
|
298
|
+
expect(removedSessionIds).not.toContain('s1');
|
|
299
|
+
});
|
|
300
|
+
it('should handle getClaudeProcesses failure gracefully', async () => {
|
|
301
|
+
mockGetClaudeProcesses.mockRejectedValue(new Error('pgrep failed'));
|
|
302
|
+
const session = makeSession({
|
|
303
|
+
session_id: 's1',
|
|
304
|
+
terminal_key: 'AUTO:some-uuid',
|
|
305
|
+
cwd: '/Users/test/project',
|
|
306
|
+
registered_at: Date.now() - 120_000,
|
|
307
|
+
});
|
|
308
|
+
sessions.set('s1', session);
|
|
309
|
+
await monitor.verifyProcesses();
|
|
310
|
+
// Should NOT remove the session (fail-safe)
|
|
311
|
+
expect(removedSessionIds).toHaveLength(0);
|
|
312
|
+
});
|
|
313
|
+
it('should match multiple PID-less sessions to multiple processes in same CWD', async () => {
|
|
314
|
+
mockIsProcessBypass.mockResolvedValue(false);
|
|
315
|
+
mockGetClaudeProcesses.mockResolvedValue([
|
|
316
|
+
{ pid: 111, tty: 'ttys001', cwd: '/Users/test/project' },
|
|
317
|
+
{ pid: 222, tty: 'ttys002', cwd: '/Users/test/project' },
|
|
318
|
+
]);
|
|
319
|
+
const s1 = makeSession({
|
|
320
|
+
session_id: 's1',
|
|
321
|
+
terminal_key: 'AUTO:uuid1',
|
|
322
|
+
cwd: '/Users/test/project',
|
|
323
|
+
registered_at: Date.now() - 120_000,
|
|
324
|
+
});
|
|
325
|
+
const s2 = makeSession({
|
|
326
|
+
session_id: 's2',
|
|
327
|
+
terminal_key: 'AUTO:uuid2',
|
|
328
|
+
cwd: '/Users/test/project',
|
|
329
|
+
registered_at: Date.now() - 120_000,
|
|
330
|
+
});
|
|
331
|
+
sessions.set('s1', s1);
|
|
332
|
+
sessions.set('s2', s2);
|
|
333
|
+
await monitor.verifyProcesses();
|
|
334
|
+
expect(removedSessionIds).toHaveLength(0);
|
|
335
|
+
// Both sessions should be enriched with PIDs
|
|
336
|
+
expect(monitor.getSessionPid(s1)).not.toBeNull();
|
|
337
|
+
expect(monitor.getSessionPid(s2)).not.toBeNull();
|
|
338
|
+
});
|
|
339
|
+
it('should skip process scan when no PID-less sessions exist', async () => {
|
|
340
|
+
mockIsProcessRunning.mockResolvedValue(true);
|
|
341
|
+
const session = makeSession({
|
|
342
|
+
session_id: 's1',
|
|
343
|
+
terminal_key: 'DISCOVERED:PID:12345',
|
|
344
|
+
});
|
|
345
|
+
sessions.set('s1', session);
|
|
346
|
+
await monitor.verifyProcesses();
|
|
347
|
+
expect(mockGetClaudeProcesses).not.toHaveBeenCalled();
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
describe('pendingBypass', () => {
|
|
351
|
+
it('should consume pending bypass for matching CWD', () => {
|
|
352
|
+
monitor.markPendingBypass('/Users/test/project');
|
|
353
|
+
expect(monitor.consumePendingBypass('/Users/test/project')).toBe(true);
|
|
354
|
+
// Second consume should return false (already consumed)
|
|
355
|
+
expect(monitor.consumePendingBypass('/Users/test/project')).toBe(false);
|
|
356
|
+
});
|
|
357
|
+
it('should normalize trailing slashes', () => {
|
|
358
|
+
monitor.markPendingBypass('/Users/test/project/');
|
|
359
|
+
expect(monitor.consumePendingBypass('/Users/test/project')).toBe(true);
|
|
360
|
+
});
|
|
361
|
+
it('should return false for non-matching CWD', () => {
|
|
362
|
+
monitor.markPendingBypass('/Users/test/project-a');
|
|
363
|
+
expect(monitor.consumePendingBypass('/Users/test/project-b')).toBe(false);
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
//# sourceMappingURL=process-monitor.test.js.map
|