parasor 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +208 -0
- package/THIRD-PARTY-NOTICES.md +138 -0
- package/bin/parasor.mjs +3 -0
- package/node_modules/@parasor/shared/dist/client.d.ts +8 -0
- package/node_modules/@parasor/shared/dist/client.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/client.js +2 -0
- package/node_modules/@parasor/shared/dist/client.js.map +1 -0
- package/node_modules/@parasor/shared/dist/drops.d.ts +24 -0
- package/node_modules/@parasor/shared/dist/drops.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/drops.js +2 -0
- package/node_modules/@parasor/shared/dist/drops.js.map +1 -0
- package/node_modules/@parasor/shared/dist/file-uploads.d.ts +56 -0
- package/node_modules/@parasor/shared/dist/file-uploads.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/file-uploads.js +13 -0
- package/node_modules/@parasor/shared/dist/file-uploads.js.map +1 -0
- package/node_modules/@parasor/shared/dist/ide-commands.d.ts +8 -0
- package/node_modules/@parasor/shared/dist/ide-commands.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/ide-commands.js +59 -0
- package/node_modules/@parasor/shared/dist/ide-commands.js.map +1 -0
- package/node_modules/@parasor/shared/dist/pane-commands.d.ts +7 -0
- package/node_modules/@parasor/shared/dist/pane-commands.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/pane-commands.js +47 -0
- package/node_modules/@parasor/shared/dist/pane-commands.js.map +1 -0
- package/node_modules/@parasor/shared/dist/pane-model.d.ts +63 -0
- package/node_modules/@parasor/shared/dist/pane-model.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/pane-model.js +89 -0
- package/node_modules/@parasor/shared/dist/pane-model.js.map +1 -0
- package/node_modules/@parasor/shared/dist/panes.d.ts +33 -0
- package/node_modules/@parasor/shared/dist/panes.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/panes.js +2 -0
- package/node_modules/@parasor/shared/dist/panes.js.map +1 -0
- package/node_modules/@parasor/shared/dist/runtime.d.ts +180 -0
- package/node_modules/@parasor/shared/dist/runtime.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/runtime.js +2 -0
- package/node_modules/@parasor/shared/dist/runtime.js.map +1 -0
- package/node_modules/@parasor/shared/dist/state.d.ts +192 -0
- package/node_modules/@parasor/shared/dist/state.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/state.js +10 -0
- package/node_modules/@parasor/shared/dist/state.js.map +1 -0
- package/node_modules/@parasor/shared/dist/terminal.d.ts +191 -0
- package/node_modules/@parasor/shared/dist/terminal.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/terminal.js +174 -0
- package/node_modules/@parasor/shared/dist/terminal.js.map +1 -0
- package/node_modules/@parasor/shared/dist/types.d.ts +13 -0
- package/node_modules/@parasor/shared/dist/types.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/types.js +13 -0
- package/node_modules/@parasor/shared/dist/types.js.map +1 -0
- package/node_modules/@parasor/shared/dist/worktree-local-files.d.ts +14 -0
- package/node_modules/@parasor/shared/dist/worktree-local-files.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/worktree-local-files.js +41 -0
- package/node_modules/@parasor/shared/dist/worktree-local-files.js.map +1 -0
- package/node_modules/@parasor/shared/dist/ws-events.d.ts +152 -0
- package/node_modules/@parasor/shared/dist/ws-events.d.ts.map +1 -0
- package/node_modules/@parasor/shared/dist/ws-events.js +2 -0
- package/node_modules/@parasor/shared/dist/ws-events.js.map +1 -0
- package/node_modules/@parasor/shared/package.json +13 -0
- package/package.json +61 -0
- package/server/agent-detector/agent-state-store.d.ts +23 -0
- package/server/agent-detector/agent-state-store.d.ts.map +1 -0
- package/server/agent-detector/agent-state-store.js +110 -0
- package/server/agent-detector/agent-state-store.js.map +1 -0
- package/server/agent-detector/detector.d.ts +80 -0
- package/server/agent-detector/detector.d.ts.map +1 -0
- package/server/agent-detector/detector.js +219 -0
- package/server/agent-detector/detector.js.map +1 -0
- package/server/agent-detector/event-map.d.ts +25 -0
- package/server/agent-detector/event-map.d.ts.map +1 -0
- package/server/agent-detector/event-map.js +136 -0
- package/server/agent-detector/event-map.js.map +1 -0
- package/server/agent-detector/manual-agent-tracker.d.ts +19 -0
- package/server/agent-detector/manual-agent-tracker.d.ts.map +1 -0
- package/server/agent-detector/manual-agent-tracker.js +123 -0
- package/server/agent-detector/manual-agent-tracker.js.map +1 -0
- package/server/agent-detector/output-eligibility.d.ts +3 -0
- package/server/agent-detector/output-eligibility.d.ts.map +1 -0
- package/server/agent-detector/output-eligibility.js +39 -0
- package/server/agent-detector/output-eligibility.js.map +1 -0
- package/server/application/files/errors.d.ts +28 -0
- package/server/application/files/errors.d.ts.map +1 -0
- package/server/application/files/errors.js +55 -0
- package/server/application/files/errors.js.map +1 -0
- package/server/application/files/local-filesystem.d.ts +36 -0
- package/server/application/files/local-filesystem.d.ts.map +1 -0
- package/server/application/files/local-filesystem.js +171 -0
- package/server/application/files/local-filesystem.js.map +1 -0
- package/server/application/files/project-file-queries.d.ts +16 -0
- package/server/application/files/project-file-queries.d.ts.map +1 -0
- package/server/application/files/project-file-queries.js +126 -0
- package/server/application/files/project-file-queries.js.map +1 -0
- package/server/application/integrations/errors.d.ts +16 -0
- package/server/application/integrations/errors.d.ts.map +1 -0
- package/server/application/integrations/errors.js +31 -0
- package/server/application/integrations/errors.js.map +1 -0
- package/server/application/integrations/hook-notify.d.ts +32 -0
- package/server/application/integrations/hook-notify.d.ts.map +1 -0
- package/server/application/integrations/hook-notify.js +118 -0
- package/server/application/integrations/hook-notify.js.map +1 -0
- package/server/application/integrations/open-url.d.ts +11 -0
- package/server/application/integrations/open-url.d.ts.map +1 -0
- package/server/application/integrations/open-url.js +23 -0
- package/server/application/integrations/open-url.js.map +1 -0
- package/server/application/ports.d.ts +11 -0
- package/server/application/ports.d.ts.map +1 -0
- package/server/application/ports.js +2 -0
- package/server/application/ports.js.map +1 -0
- package/server/application/workspace/errors.d.ts +21 -0
- package/server/application/workspace/errors.d.ts.map +1 -0
- package/server/application/workspace/errors.js +21 -0
- package/server/application/workspace/errors.js.map +1 -0
- package/server/application/workspace/pane-commands.d.ts +47 -0
- package/server/application/workspace/pane-commands.d.ts.map +1 -0
- package/server/application/workspace/pane-commands.js +193 -0
- package/server/application/workspace/pane-commands.js.map +1 -0
- package/server/application/workspace/project-commands.d.ts +34 -0
- package/server/application/workspace/project-commands.d.ts.map +1 -0
- package/server/application/workspace/project-commands.js +81 -0
- package/server/application/workspace/project-commands.js.map +1 -0
- package/server/application/workspace/project-queries.d.ts +21 -0
- package/server/application/workspace/project-queries.d.ts.map +1 -0
- package/server/application/workspace/project-queries.js +216 -0
- package/server/application/workspace/project-queries.js.map +1 -0
- package/server/application/workspace/session-commands.d.ts +24 -0
- package/server/application/workspace/session-commands.d.ts.map +1 -0
- package/server/application/workspace/session-commands.js +123 -0
- package/server/application/workspace/session-commands.js.map +1 -0
- package/server/application/workspace/session-queries.d.ts +14 -0
- package/server/application/workspace/session-queries.d.ts.map +1 -0
- package/server/application/workspace/session-queries.js +47 -0
- package/server/application/workspace/session-queries.js.map +1 -0
- package/server/application/workspace/worktree-commands.d.ts +98 -0
- package/server/application/workspace/worktree-commands.d.ts.map +1 -0
- package/server/application/workspace/worktree-commands.js +323 -0
- package/server/application/workspace/worktree-commands.js.map +1 -0
- package/server/application/workspace/worktree-local-files.d.ts +11 -0
- package/server/application/workspace/worktree-local-files.d.ts.map +1 -0
- package/server/application/workspace/worktree-local-files.js +223 -0
- package/server/application/workspace/worktree-local-files.js.map +1 -0
- package/server/application/workspace/worktree-reconcile.d.ts +21 -0
- package/server/application/workspace/worktree-reconcile.d.ts.map +1 -0
- package/server/application/workspace/worktree-reconcile.js +49 -0
- package/server/application/workspace/worktree-reconcile.js.map +1 -0
- package/server/auth/origin.d.ts +17 -0
- package/server/auth/origin.d.ts.map +1 -0
- package/server/auth/origin.js +93 -0
- package/server/auth/origin.js.map +1 -0
- package/server/auth/pairing-token.d.ts +28 -0
- package/server/auth/pairing-token.d.ts.map +1 -0
- package/server/auth/pairing-token.js +46 -0
- package/server/auth/pairing-token.js.map +1 -0
- package/server/auth/token-exchange.d.ts +11 -0
- package/server/auth/token-exchange.d.ts.map +1 -0
- package/server/auth/token-exchange.js +27 -0
- package/server/auth/token-exchange.js.map +1 -0
- package/server/auth/token.d.ts +18 -0
- package/server/auth/token.d.ts.map +1 -0
- package/server/auth/token.js +115 -0
- package/server/auth/token.js.map +1 -0
- package/server/bootstrap/create-app-server.d.ts +49 -0
- package/server/bootstrap/create-app-server.d.ts.map +1 -0
- package/server/bootstrap/create-app-server.js +154 -0
- package/server/bootstrap/create-app-server.js.map +1 -0
- package/server/bootstrap/project-runtime.d.ts +29 -0
- package/server/bootstrap/project-runtime.d.ts.map +1 -0
- package/server/bootstrap/project-runtime.js +253 -0
- package/server/bootstrap/project-runtime.js.map +1 -0
- package/server/bootstrap/pty-env.d.ts +3 -0
- package/server/bootstrap/pty-env.d.ts.map +1 -0
- package/server/bootstrap/pty-env.js +15 -0
- package/server/bootstrap/pty-env.js.map +1 -0
- package/server/bootstrap/reconcile-state.d.ts +16 -0
- package/server/bootstrap/reconcile-state.d.ts.map +1 -0
- package/server/bootstrap/reconcile-state.js +73 -0
- package/server/bootstrap/reconcile-state.js.map +1 -0
- package/server/bootstrap/runtime-loops.d.ts +51 -0
- package/server/bootstrap/runtime-loops.d.ts.map +1 -0
- package/server/bootstrap/runtime-loops.js +189 -0
- package/server/bootstrap/runtime-loops.js.map +1 -0
- package/server/bootstrap/runtime-port.d.ts +4 -0
- package/server/bootstrap/runtime-port.d.ts.map +1 -0
- package/server/bootstrap/runtime-port.js +52 -0
- package/server/bootstrap/runtime-port.js.map +1 -0
- package/server/bootstrap/safety-gate.d.ts +22 -0
- package/server/bootstrap/safety-gate.d.ts.map +1 -0
- package/server/bootstrap/safety-gate.js +43 -0
- package/server/bootstrap/safety-gate.js.map +1 -0
- package/server/bootstrap/shutdown-marker.d.ts +35 -0
- package/server/bootstrap/shutdown-marker.d.ts.map +1 -0
- package/server/bootstrap/shutdown-marker.js +100 -0
- package/server/bootstrap/shutdown-marker.js.map +1 -0
- package/server/bootstrap/shutdown-runtime.d.ts +62 -0
- package/server/bootstrap/shutdown-runtime.d.ts.map +1 -0
- package/server/bootstrap/shutdown-runtime.js +66 -0
- package/server/bootstrap/shutdown-runtime.js.map +1 -0
- package/server/bootstrap/startup-banner.d.ts +34 -0
- package/server/bootstrap/startup-banner.d.ts.map +1 -0
- package/server/bootstrap/startup-banner.js +114 -0
- package/server/bootstrap/startup-banner.js.map +1 -0
- package/server/bootstrap/wire-runtime.d.ts +42 -0
- package/server/bootstrap/wire-runtime.d.ts.map +1 -0
- package/server/bootstrap/wire-runtime.js +205 -0
- package/server/bootstrap/wire-runtime.js.map +1 -0
- package/server/cli/help.d.ts +3 -0
- package/server/cli/help.d.ts.map +1 -0
- package/server/cli/help.js +39 -0
- package/server/cli/help.js.map +1 -0
- package/server/cli/hook-client.d.ts +12 -0
- package/server/cli/hook-client.d.ts.map +1 -0
- package/server/cli/hook-client.js +81 -0
- package/server/cli/hook-client.js.map +1 -0
- package/server/cli/hook.d.ts +3 -0
- package/server/cli/hook.d.ts.map +1 -0
- package/server/cli/hook.js +152 -0
- package/server/cli/hook.js.map +1 -0
- package/server/cli/main.d.ts +2 -0
- package/server/cli/main.d.ts.map +1 -0
- package/server/cli/main.js +133 -0
- package/server/cli/main.js.map +1 -0
- package/server/cli/notify.d.ts +19 -0
- package/server/cli/notify.d.ts.map +1 -0
- package/server/cli/notify.js +77 -0
- package/server/cli/notify.js.map +1 -0
- package/server/cli/open.d.ts +2 -0
- package/server/cli/open.d.ts.map +1 -0
- package/server/cli/open.js +47 -0
- package/server/cli/open.js.map +1 -0
- package/server/cli/probe-daemon-version.d.ts +46 -0
- package/server/cli/probe-daemon-version.d.ts.map +1 -0
- package/server/cli/probe-daemon-version.js +167 -0
- package/server/cli/probe-daemon-version.js.map +1 -0
- package/server/cli/pty-host.d.ts +36 -0
- package/server/cli/pty-host.d.ts.map +1 -0
- package/server/cli/pty-host.js +390 -0
- package/server/cli/pty-host.js.map +1 -0
- package/server/cli/qr.d.ts +2 -0
- package/server/cli/qr.d.ts.map +1 -0
- package/server/cli/qr.js +70 -0
- package/server/cli/qr.js.map +1 -0
- package/server/cli/restart-confirm.d.ts +33 -0
- package/server/cli/restart-confirm.d.ts.map +1 -0
- package/server/cli/restart-confirm.js +107 -0
- package/server/cli/restart-confirm.js.map +1 -0
- package/server/cli/restart.d.ts +17 -0
- package/server/cli/restart.d.ts.map +1 -0
- package/server/cli/restart.js +145 -0
- package/server/cli/restart.js.map +1 -0
- package/server/cli/service-darwin.d.ts +70 -0
- package/server/cli/service-darwin.d.ts.map +1 -0
- package/server/cli/service-darwin.js +562 -0
- package/server/cli/service-darwin.js.map +1 -0
- package/server/cli/service-linux.d.ts +81 -0
- package/server/cli/service-linux.d.ts.map +1 -0
- package/server/cli/service-linux.js +412 -0
- package/server/cli/service-linux.js.map +1 -0
- package/server/cli/service.d.ts +33 -0
- package/server/cli/service.d.ts.map +1 -0
- package/server/cli/service.js +133 -0
- package/server/cli/service.js.map +1 -0
- package/server/cli/shim-installer.d.ts +35 -0
- package/server/cli/shim-installer.d.ts.map +1 -0
- package/server/cli/shim-installer.js +662 -0
- package/server/cli/shim-installer.js.map +1 -0
- package/server/cli/shim-open.d.ts +2 -0
- package/server/cli/shim-open.d.ts.map +1 -0
- package/server/cli/shim-open.js +128 -0
- package/server/cli/shim-open.js.map +1 -0
- package/server/cli/shutdown-deps.d.ts +41 -0
- package/server/cli/shutdown-deps.d.ts.map +1 -0
- package/server/cli/shutdown-deps.js +158 -0
- package/server/cli/shutdown-deps.js.map +1 -0
- package/server/cli/stop.d.ts +11 -0
- package/server/cli/stop.d.ts.map +1 -0
- package/server/cli/stop.js +123 -0
- package/server/cli/stop.js.map +1 -0
- package/server/cli/unknown-command.d.ts +8 -0
- package/server/cli/unknown-command.d.ts.map +1 -0
- package/server/cli/unknown-command.js +14 -0
- package/server/cli/unknown-command.js.map +1 -0
- package/server/debug/agent-status-recorder.d.ts +57 -0
- package/server/debug/agent-status-recorder.d.ts.map +1 -0
- package/server/debug/agent-status-recorder.js +209 -0
- package/server/debug/agent-status-recorder.js.map +1 -0
- package/server/debug/terminal-trace-recorder.d.ts +41 -0
- package/server/debug/terminal-trace-recorder.d.ts.map +1 -0
- package/server/debug/terminal-trace-recorder.js +112 -0
- package/server/debug/terminal-trace-recorder.js.map +1 -0
- package/server/fonts/catalog.d.ts +35 -0
- package/server/fonts/catalog.d.ts.map +1 -0
- package/server/fonts/catalog.js +76 -0
- package/server/fonts/catalog.js.map +1 -0
- package/server/fonts/installer.d.ts +45 -0
- package/server/fonts/installer.d.ts.map +1 -0
- package/server/fonts/installer.js +201 -0
- package/server/fonts/installer.js.map +1 -0
- package/server/fonts/routes.d.ts +4 -0
- package/server/fonts/routes.d.ts.map +1 -0
- package/server/fonts/routes.js +91 -0
- package/server/fonts/routes.js.map +1 -0
- package/server/fs/drops.d.ts +50 -0
- package/server/fs/drops.d.ts.map +1 -0
- package/server/fs/drops.js +137 -0
- package/server/fs/drops.js.map +1 -0
- package/server/fs/file-uploads.d.ts +57 -0
- package/server/fs/file-uploads.d.ts.map +1 -0
- package/server/fs/file-uploads.js +214 -0
- package/server/fs/file-uploads.js.map +1 -0
- package/server/fs/file-watcher.d.ts +22 -0
- package/server/fs/file-watcher.d.ts.map +1 -0
- package/server/fs/file-watcher.js +105 -0
- package/server/fs/file-watcher.js.map +1 -0
- package/server/fs/git-watcher.d.ts +76 -0
- package/server/fs/git-watcher.d.ts.map +1 -0
- package/server/fs/git-watcher.js +356 -0
- package/server/fs/git-watcher.js.map +1 -0
- package/server/fs/media.d.ts +35 -0
- package/server/fs/media.d.ts.map +1 -0
- package/server/fs/media.js +271 -0
- package/server/fs/media.js.map +1 -0
- package/server/fs/service.d.ts +97 -0
- package/server/fs/service.d.ts.map +1 -0
- package/server/fs/service.js +306 -0
- package/server/fs/service.js.map +1 -0
- package/server/fs/upload-staging.d.ts +76 -0
- package/server/fs/upload-staging.d.ts.map +1 -0
- package/server/fs/upload-staging.js +283 -0
- package/server/fs/upload-staging.js.map +1 -0
- package/server/fs/watcher-lifecycle.d.ts +24 -0
- package/server/fs/watcher-lifecycle.d.ts.map +1 -0
- package/server/fs/watcher-lifecycle.js +105 -0
- package/server/fs/watcher-lifecycle.js.map +1 -0
- package/server/index.d.ts +17 -0
- package/server/index.d.ts.map +1 -0
- package/server/index.js +510 -0
- package/server/index.js.map +1 -0
- package/server/ipc/socket-server.d.ts +28 -0
- package/server/ipc/socket-server.d.ts.map +1 -0
- package/server/ipc/socket-server.js +206 -0
- package/server/ipc/socket-server.js.map +1 -0
- package/server/lib/git-exec.d.ts +38 -0
- package/server/lib/git-exec.d.ts.map +1 -0
- package/server/lib/git-exec.js +96 -0
- package/server/lib/git-exec.js.map +1 -0
- package/server/lib/open-in-ide.d.ts +22 -0
- package/server/lib/open-in-ide.d.ts.map +1 -0
- package/server/lib/open-in-ide.js +90 -0
- package/server/lib/open-in-ide.js.map +1 -0
- package/server/lib/open-in-os.d.ts +20 -0
- package/server/lib/open-in-os.d.ts.map +1 -0
- package/server/lib/open-in-os.js +51 -0
- package/server/lib/open-in-os.js.map +1 -0
- package/server/lib/path.d.ts +2 -0
- package/server/lib/path.d.ts.map +1 -0
- package/server/lib/path.js +10 -0
- package/server/lib/path.js.map +1 -0
- package/server/lib/promise-mutex.d.ts +7 -0
- package/server/lib/promise-mutex.d.ts.map +1 -0
- package/server/lib/promise-mutex.js +26 -0
- package/server/lib/promise-mutex.js.map +1 -0
- package/server/lib/sd-notify.d.ts +8 -0
- package/server/lib/sd-notify.d.ts.map +1 -0
- package/server/lib/sd-notify.js +27 -0
- package/server/lib/sd-notify.js.map +1 -0
- package/server/net/local-machine.d.ts +7 -0
- package/server/net/local-machine.d.ts.map +1 -0
- package/server/net/local-machine.js +79 -0
- package/server/net/local-machine.js.map +1 -0
- package/server/net/reachable-host.d.ts +12 -0
- package/server/net/reachable-host.d.ts.map +1 -0
- package/server/net/reachable-host.js +25 -0
- package/server/net/reachable-host.js.map +1 -0
- package/server/network/endpoints.d.ts +14 -0
- package/server/network/endpoints.d.ts.map +1 -0
- package/server/network/endpoints.js +104 -0
- package/server/network/endpoints.js.map +1 -0
- package/server/network/qr.d.ts +16 -0
- package/server/network/qr.d.ts.map +1 -0
- package/server/network/qr.js +89 -0
- package/server/network/qr.js.map +1 -0
- package/server/port-forwarder/forwarder.d.ts +47 -0
- package/server/port-forwarder/forwarder.d.ts.map +1 -0
- package/server/port-forwarder/forwarder.js +159 -0
- package/server/port-forwarder/forwarder.js.map +1 -0
- package/server/port-scanner/scanner.d.ts +24 -0
- package/server/port-scanner/scanner.d.ts.map +1 -0
- package/server/port-scanner/scanner.js +193 -0
- package/server/port-scanner/scanner.js.map +1 -0
- package/server/pty/connection-lifecycle.d.ts +50 -0
- package/server/pty/connection-lifecycle.d.ts.map +1 -0
- package/server/pty/connection-lifecycle.js +113 -0
- package/server/pty/connection-lifecycle.js.map +1 -0
- package/server/pty/daemon-connect.d.ts +52 -0
- package/server/pty/daemon-connect.d.ts.map +1 -0
- package/server/pty/daemon-connect.js +62 -0
- package/server/pty/daemon-connect.js.map +1 -0
- package/server/pty/handshake-timeout-race.d.ts +22 -0
- package/server/pty/handshake-timeout-race.d.ts.map +1 -0
- package/server/pty/handshake-timeout-race.js +23 -0
- package/server/pty/handshake-timeout-race.js.map +1 -0
- package/server/pty/headless-replay-snapshot.d.ts +28 -0
- package/server/pty/headless-replay-snapshot.d.ts.map +1 -0
- package/server/pty/headless-replay-snapshot.js +285 -0
- package/server/pty/headless-replay-snapshot.js.map +1 -0
- package/server/pty/headless-terminal-state-cache.d.ts +31 -0
- package/server/pty/headless-terminal-state-cache.d.ts.map +1 -0
- package/server/pty/headless-terminal-state-cache.js +119 -0
- package/server/pty/headless-terminal-state-cache.js.map +1 -0
- package/server/pty/hello-ack-validator.d.ts +15 -0
- package/server/pty/hello-ack-validator.d.ts.map +1 -0
- package/server/pty/hello-ack-validator.js +30 -0
- package/server/pty/hello-ack-validator.js.map +1 -0
- package/server/pty/host-daemon/bootstrap.d.ts +100 -0
- package/server/pty/host-daemon/bootstrap.d.ts.map +1 -0
- package/server/pty/host-daemon/bootstrap.js +611 -0
- package/server/pty/host-daemon/bootstrap.js.map +1 -0
- package/server/pty/host-daemon/daemon.d.ts +111 -0
- package/server/pty/host-daemon/daemon.d.ts.map +1 -0
- package/server/pty/host-daemon/daemon.js +648 -0
- package/server/pty/host-daemon/daemon.js.map +1 -0
- package/server/pty/host-daemon/entry.d.ts +3 -0
- package/server/pty/host-daemon/entry.d.ts.map +1 -0
- package/server/pty/host-daemon/entry.js +72 -0
- package/server/pty/host-daemon/entry.js.map +1 -0
- package/server/pty/host-daemon/lockfile.d.ts +12 -0
- package/server/pty/host-daemon/lockfile.d.ts.map +1 -0
- package/server/pty/host-daemon/lockfile.js +120 -0
- package/server/pty/host-daemon/lockfile.js.map +1 -0
- package/server/pty/host-daemon/mode-marker.d.ts +77 -0
- package/server/pty/host-daemon/mode-marker.d.ts.map +1 -0
- package/server/pty/host-daemon/mode-marker.js +228 -0
- package/server/pty/host-daemon/mode-marker.js.map +1 -0
- package/server/pty/host-daemon/orphan-cleanup.d.ts +42 -0
- package/server/pty/host-daemon/orphan-cleanup.d.ts.map +1 -0
- package/server/pty/host-daemon/orphan-cleanup.js +84 -0
- package/server/pty/host-daemon/orphan-cleanup.js.map +1 -0
- package/server/pty/host-daemon/paths.d.ts +10 -0
- package/server/pty/host-daemon/paths.d.ts.map +1 -0
- package/server/pty/host-daemon/paths.js +56 -0
- package/server/pty/host-daemon/paths.js.map +1 -0
- package/server/pty/host-daemon/server-connection.d.ts +44 -0
- package/server/pty/host-daemon/server-connection.d.ts.map +1 -0
- package/server/pty/host-daemon/server-connection.js +100 -0
- package/server/pty/host-daemon/server-connection.js.map +1 -0
- package/server/pty/host-daemon/service-detection.d.ts +24 -0
- package/server/pty/host-daemon/service-detection.d.ts.map +1 -0
- package/server/pty/host-daemon/service-detection.js +16 -0
- package/server/pty/host-daemon/service-detection.js.map +1 -0
- package/server/pty/host-daemon/socket-ready.d.ts +22 -0
- package/server/pty/host-daemon/socket-ready.d.ts.map +1 -0
- package/server/pty/host-daemon/socket-ready.js +33 -0
- package/server/pty/host-daemon/socket-ready.js.map +1 -0
- package/server/pty/host-daemon/spawn-daemon.d.ts +45 -0
- package/server/pty/host-daemon/spawn-daemon.d.ts.map +1 -0
- package/server/pty/host-daemon/spawn-daemon.js +155 -0
- package/server/pty/host-daemon/spawn-daemon.js.map +1 -0
- package/server/pty/host-daemon/terminate-daemon.d.ts +16 -0
- package/server/pty/host-daemon/terminate-daemon.d.ts.map +1 -0
- package/server/pty/host-daemon/terminate-daemon.js +128 -0
- package/server/pty/host-daemon/terminate-daemon.js.map +1 -0
- package/server/pty/host-protocol/frames.d.ts +110 -0
- package/server/pty/host-protocol/frames.d.ts.map +1 -0
- package/server/pty/host-protocol/frames.js +255 -0
- package/server/pty/host-protocol/frames.js.map +1 -0
- package/server/pty/host-protocol/messages.d.ts +140 -0
- package/server/pty/host-protocol/messages.d.ts.map +1 -0
- package/server/pty/host-protocol/messages.js +117 -0
- package/server/pty/host-protocol/messages.js.map +1 -0
- package/server/pty/host.d.ts +273 -0
- package/server/pty/host.d.ts.map +1 -0
- package/server/pty/host.js +184 -0
- package/server/pty/host.js.map +1 -0
- package/server/pty/in-process-host.d.ts +224 -0
- package/server/pty/in-process-host.d.ts.map +1 -0
- package/server/pty/in-process-host.js +1183 -0
- package/server/pty/in-process-host.js.map +1 -0
- package/server/pty/osc7-lifecycle.d.ts +8 -0
- package/server/pty/osc7-lifecycle.d.ts.map +1 -0
- package/server/pty/osc7-lifecycle.js +22 -0
- package/server/pty/osc7-lifecycle.js.map +1 -0
- package/server/pty/osc7-parser.d.ts +23 -0
- package/server/pty/osc7-parser.d.ts.map +1 -0
- package/server/pty/osc7-parser.js +91 -0
- package/server/pty/osc7-parser.js.map +1 -0
- package/server/pty/remote-host.d.ts +188 -0
- package/server/pty/remote-host.d.ts.map +1 -0
- package/server/pty/remote-host.js +810 -0
- package/server/pty/remote-host.js.map +1 -0
- package/server/pty/request-correlator.d.ts +59 -0
- package/server/pty/request-correlator.d.ts.map +1 -0
- package/server/pty/request-correlator.js +75 -0
- package/server/pty/request-correlator.js.map +1 -0
- package/server/pty/scrollback-log.d.ts +130 -0
- package/server/pty/scrollback-log.d.ts.map +1 -0
- package/server/pty/scrollback-log.js +344 -0
- package/server/pty/scrollback-log.js.map +1 -0
- package/server/pty/scrollback-sanitize.d.ts +2 -0
- package/server/pty/scrollback-sanitize.d.ts.map +1 -0
- package/server/pty/scrollback-sanitize.js +54 -0
- package/server/pty/scrollback-sanitize.js.map +1 -0
- package/server/pty/session-mirror.d.ts +67 -0
- package/server/pty/session-mirror.d.ts.map +1 -0
- package/server/pty/session-mirror.js +112 -0
- package/server/pty/session-mirror.js.map +1 -0
- package/server/pty/session-policy.d.ts +85 -0
- package/server/pty/session-policy.d.ts.map +1 -0
- package/server/pty/session-policy.js +186 -0
- package/server/pty/session-policy.js.map +1 -0
- package/server/pty/version-mismatch-recovery.d.ts +71 -0
- package/server/pty/version-mismatch-recovery.d.ts.map +1 -0
- package/server/pty/version-mismatch-recovery.js +63 -0
- package/server/pty/version-mismatch-recovery.js.map +1 -0
- package/server/routes/debug-agent-status.d.ts +5 -0
- package/server/routes/debug-agent-status.d.ts.map +1 -0
- package/server/routes/debug-agent-status.js +22 -0
- package/server/routes/debug-agent-status.js.map +1 -0
- package/server/routes/debug-diagnostics.d.ts +8 -0
- package/server/routes/debug-diagnostics.d.ts.map +1 -0
- package/server/routes/debug-diagnostics.js +34 -0
- package/server/routes/debug-diagnostics.js.map +1 -0
- package/server/routes/debug-terminal-trace.d.ts +16 -0
- package/server/routes/debug-terminal-trace.d.ts.map +1 -0
- package/server/routes/debug-terminal-trace.js +545 -0
- package/server/routes/debug-terminal-trace.js.map +1 -0
- package/server/routes/drops.d.ts +13 -0
- package/server/routes/drops.d.ts.map +1 -0
- package/server/routes/drops.js +126 -0
- package/server/routes/drops.js.map +1 -0
- package/server/routes/file-uploads.d.ts +9 -0
- package/server/routes/file-uploads.d.ts.map +1 -0
- package/server/routes/file-uploads.js +119 -0
- package/server/routes/file-uploads.js.map +1 -0
- package/server/routes/files.d.ts +9 -0
- package/server/routes/files.d.ts.map +1 -0
- package/server/routes/files.js +441 -0
- package/server/routes/files.js.map +1 -0
- package/server/routes/filesystem.d.ts +6 -0
- package/server/routes/filesystem.d.ts.map +1 -0
- package/server/routes/filesystem.js +76 -0
- package/server/routes/filesystem.js.map +1 -0
- package/server/routes/git.d.ts +38 -0
- package/server/routes/git.d.ts.map +1 -0
- package/server/routes/git.js +585 -0
- package/server/routes/git.js.map +1 -0
- package/server/routes/healthz.d.ts +8 -0
- package/server/routes/healthz.d.ts.map +1 -0
- package/server/routes/healthz.js +24 -0
- package/server/routes/healthz.js.map +1 -0
- package/server/routes/hook.d.ts +14 -0
- package/server/routes/hook.d.ts.map +1 -0
- package/server/routes/hook.js +86 -0
- package/server/routes/hook.js.map +1 -0
- package/server/routes/ide-commands.d.ts +9 -0
- package/server/routes/ide-commands.d.ts.map +1 -0
- package/server/routes/ide-commands.js +23 -0
- package/server/routes/ide-commands.js.map +1 -0
- package/server/routes/lib/resolve-worktree.d.ts +30 -0
- package/server/routes/lib/resolve-worktree.d.ts.map +1 -0
- package/server/routes/lib/resolve-worktree.js +35 -0
- package/server/routes/lib/resolve-worktree.js.map +1 -0
- package/server/routes/open.d.ts +4 -0
- package/server/routes/open.d.ts.map +1 -0
- package/server/routes/open.js +21 -0
- package/server/routes/open.js.map +1 -0
- package/server/routes/pane-commands.d.ts +9 -0
- package/server/routes/pane-commands.d.ts.map +1 -0
- package/server/routes/pane-commands.js +23 -0
- package/server/routes/pane-commands.js.map +1 -0
- package/server/routes/projects.d.ts +15 -0
- package/server/routes/projects.d.ts.map +1 -0
- package/server/routes/projects.js +363 -0
- package/server/routes/projects.js.map +1 -0
- package/server/routes/server-notices.d.ts +4 -0
- package/server/routes/server-notices.d.ts.map +1 -0
- package/server/routes/server-notices.js +29 -0
- package/server/routes/server-notices.js.map +1 -0
- package/server/routes/service-config.d.ts +11 -0
- package/server/routes/service-config.d.ts.map +1 -0
- package/server/routes/service-config.js +64 -0
- package/server/routes/service-config.js.map +1 -0
- package/server/routes/sessions.d.ts +7 -0
- package/server/routes/sessions.d.ts.map +1 -0
- package/server/routes/sessions.js +218 -0
- package/server/routes/sessions.js.map +1 -0
- package/server/service/caffeinate.d.ts +33 -0
- package/server/service/caffeinate.d.ts.map +1 -0
- package/server/service/caffeinate.js +72 -0
- package/server/service/caffeinate.js.map +1 -0
- package/server/state/app-state.d.ts +167 -0
- package/server/state/app-state.d.ts.map +1 -0
- package/server/state/app-state.js +335 -0
- package/server/state/app-state.js.map +1 -0
- package/server/state/project-manager.d.ts +30 -0
- package/server/state/project-manager.d.ts.map +1 -0
- package/server/state/project-manager.js +128 -0
- package/server/state/project-manager.js.map +1 -0
- package/server/state/server-notices.d.ts +12 -0
- package/server/state/server-notices.d.ts.map +1 -0
- package/server/state/server-notices.js +32 -0
- package/server/state/server-notices.js.map +1 -0
- package/server/state/worktree-cache.d.ts +25 -0
- package/server/state/worktree-cache.d.ts.map +1 -0
- package/server/state/worktree-cache.js +53 -0
- package/server/state/worktree-cache.js.map +1 -0
- package/server/ws/events.d.ts +45 -0
- package/server/ws/events.d.ts.map +1 -0
- package/server/ws/events.js +134 -0
- package/server/ws/events.js.map +1 -0
- package/server/ws/keepalive.d.ts +27 -0
- package/server/ws/keepalive.d.ts.map +1 -0
- package/server/ws/keepalive.js +63 -0
- package/server/ws/keepalive.js.map +1 -0
- package/server/ws/terminal-attach.d.ts +13 -0
- package/server/ws/terminal-attach.d.ts.map +1 -0
- package/server/ws/terminal-attach.js +233 -0
- package/server/ws/terminal-attach.js.map +1 -0
- package/server/ws/terminal-flow.d.ts +17 -0
- package/server/ws/terminal-flow.d.ts.map +1 -0
- package/server/ws/terminal-flow.js +64 -0
- package/server/ws/terminal-flow.js.map +1 -0
- package/server/ws/terminal.d.ts +69 -0
- package/server/ws/terminal.d.ts.map +1 -0
- package/server/ws/terminal.js +311 -0
- package/server/ws/terminal.js.map +1 -0
- package/web/assets/EditorPane-CzzT3iYY.js +123 -0
- package/web/assets/SymbolsNerdFontMono-Regular-CwEZqMeU.woff2 +0 -0
- package/web/assets/TerminalPane-CjbYzePr.js +68 -0
- package/web/assets/TerminalPane-DkTCHfhq.css +1 -0
- package/web/assets/file-icons-JBi09j0r.js +6 -0
- package/web/assets/index-CTTkRpnn.css +2 -0
- package/web/assets/index-CmhewzMp.js +34 -0
- package/web/assets/session-resume-7f-tB-ZU.js +2 -0
- package/web/assets/terminal-trace-PuuFRybC.js +2803 -0
- package/web/assets/useVirtualKeyboard-DgJb9u9d.js +1 -0
- package/web/index.html +52 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../../src/pty/host-daemon/entry.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* -- `parasor-pty-host` CLI entry point.
|
|
4
|
+
*
|
|
5
|
+
* Boots a daemon, runs until SIGTERM/SIGINT, exits 0 on graceful stop or
|
|
6
|
+
* non-zero on bootstrap failure. No subcommands yet; this entry is what
|
|
7
|
+
* lifecycle commands invoke under the hood (or what a launchd/systemd unit
|
|
8
|
+
* wraps directly).
|
|
9
|
+
*
|
|
10
|
+
* Logging: opt-in via `PARASOR_PTY_HOST_DEBUG=1`. Default is silent so
|
|
11
|
+
* background-spawned daemons don't pollute the terminal that started
|
|
12
|
+
* them.
|
|
13
|
+
*/
|
|
14
|
+
import { appendFileSync } from "node:fs";
|
|
15
|
+
import { UploadStaging } from "../../fs/upload-staging.js";
|
|
16
|
+
import { bootstrapDaemon } from "./bootstrap.js";
|
|
17
|
+
import { DaemonAlreadyRunningError } from "./lockfile.js";
|
|
18
|
+
import { resolveDaemonPaths } from "./paths.js";
|
|
19
|
+
async function main() {
|
|
20
|
+
const paths = resolveDaemonPaths();
|
|
21
|
+
const debug = process.env.PARASOR_PTY_HOST_DEBUG === "1";
|
|
22
|
+
const log = (line) => {
|
|
23
|
+
const stamped = `${new Date().toISOString()} [pty-host] ${line}\n`;
|
|
24
|
+
try {
|
|
25
|
+
appendFileSync(paths.logFile, stamped);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
/* log file write best-effort */
|
|
29
|
+
}
|
|
30
|
+
if (debug)
|
|
31
|
+
process.stderr.write(stamped);
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
/*
|
|
35
|
+
* Upload staging isolation -- daemon-side per-session env injection.
|
|
36
|
+
* The daemon is its own process so it cannot share the server's
|
|
37
|
+
* `UploadStaging` instance; we instantiate one here purely for the
|
|
38
|
+
* canonical `uploadsDir` path. The constructor's symlink/owner
|
|
39
|
+
* guards run again as a defence-in-depth check (cheap idempotent
|
|
40
|
+
* mkdir + lstat). The L3 sweep stays in the server process -- the
|
|
41
|
+
* daemon never ticks `sweepStale` to avoid duplicate work, but it
|
|
42
|
+
* does need the canonical path so `InProcessPtyHost.buildSessionEnv`
|
|
43
|
+
* can stamp `PARASOR_UPLOAD_DIR=<dir>/<sid>` on every spawned PTY.
|
|
44
|
+
*/
|
|
45
|
+
const uploadStaging = new UploadStaging({});
|
|
46
|
+
const running = await bootstrapDaemon({
|
|
47
|
+
paths,
|
|
48
|
+
log,
|
|
49
|
+
uploadsDir: uploadStaging.uploadsDir,
|
|
50
|
+
});
|
|
51
|
+
log(`pid=${process.pid} ready socket=${running.paths.socketPath}`);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err instanceof DaemonAlreadyRunningError) {
|
|
55
|
+
// R4 -- when foreground bootstrap collides with an
|
|
56
|
+
// installed daemon the bare error doesn't tell the user *why* their
|
|
57
|
+
// socket is busy or how to coexist. Emit an explicit hint pointing
|
|
58
|
+
// at `parasor service status` (verifies installed daemon ownership)
|
|
59
|
+
// and the per-PID opt-in (`PARASOR_PTY_SOCK_PER_PID=1`) which gives
|
|
60
|
+
// the foreground process its own socket basename.
|
|
61
|
+
process.stderr.write(`${err.message}\n`);
|
|
62
|
+
process.stderr.write("\nThe installed parasor service may own this socket. Try:\n");
|
|
63
|
+
process.stderr.write(" parasor service status # check installed service state\n");
|
|
64
|
+
process.stderr.write(" PARASOR_PTY_SOCK_PER_PID=1 parasor server # foreground daemon on a per-PID socket\n");
|
|
65
|
+
process.exit(2);
|
|
66
|
+
}
|
|
67
|
+
process.stderr.write(`parasor-pty-host fatal: ${err.stack ?? err}\n`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
void main();
|
|
72
|
+
//# sourceMappingURL=entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../src/pty/host-daemon/entry.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,KAAK,UAAU,IAAI;IACjB,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,GAAG,CAAC;IAEzD,MAAM,GAAG,GAAG,CAAC,IAAY,EAAQ,EAAE;QACjC,MAAM,OAAO,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,eAAe,IAAI,IAAI,CAAC;QACnE,IAAI,CAAC;YACH,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,gCAAgC;QAClC,CAAC;QACD,IAAI,KAAK;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC,CAAC;IAEF,IAAI,CAAC;QACH;;;;;;;;;;WAUG;QACH,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;YACpC,KAAK;YACL,GAAG;YACH,UAAU,EAAE,aAAa,CAAC,UAAU;SACrC,CAAC,CAAC;QACH,GAAG,CAAC,OAAO,OAAO,CAAC,GAAG,iBAAiB,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,yBAAyB,EAAE,CAAC;YAC7C,oDAAoD;YACpD,oEAAoE;YACpE,mEAAmE;YACnE,oEAAoE;YACpE,oEAAoE;YACpE,kDAAkD;YAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;YACzC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6DAA6D,CAC9D,CAAC;YACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oEAAoE,CACrE,CAAC;YACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,yFAAyF,CAC1F,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,2BAA4B,GAAa,CAAC,KAAK,IAAI,GAAG,IAAI,CAC3D,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,KAAK,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DaemonPaths } from "./paths.js";
|
|
2
|
+
export interface DaemonLock {
|
|
3
|
+
pidFile: string;
|
|
4
|
+
release: () => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare class DaemonAlreadyRunningError extends Error {
|
|
7
|
+
readonly pid: number;
|
|
8
|
+
readonly pidFile: string;
|
|
9
|
+
constructor(pid: number, pidFile: string);
|
|
10
|
+
}
|
|
11
|
+
export declare function acquireDaemonLock(paths: DaemonPaths, ourPid?: number): Promise<DaemonLock>;
|
|
12
|
+
//# sourceMappingURL=lockfile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockfile.d.ts","sourceRoot":"","sources":["../../../src/pty/host-daemon/lockfile.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAEhD,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM;gBADf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM;CAK3B;AAED,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,MAAoB,GAC3B,OAAO,CAAC,UAAU,CAAC,CA+CrB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* -- daemon single-instance enforcement.
|
|
3
|
+
*
|
|
4
|
+
* `proper-lockfile` advisory flock on `paths.lockFile` is the *sole*
|
|
5
|
+
* source of truth for "another daemon owns this runtime dir". It is held
|
|
6
|
+
* across the daemon's whole lifetime and released either by graceful
|
|
7
|
+
* shutdown or by stale-timeout (60s) if the holder dies.
|
|
8
|
+
*
|
|
9
|
+
* The pidfile is purely informational: humans (and `parasor pty-host
|
|
10
|
+
* status`) read it to find the daemon's PID; the recorded pid liveness
|
|
11
|
+
* probe (`kill(pid, 0)`) is only consulted to populate
|
|
12
|
+
* `DaemonAlreadyRunningError.pid` for nicer error messages. We do *not*
|
|
13
|
+
* cross-validate the pidfile post-flock -- once flock returned success,
|
|
14
|
+
* the recycled-PID corner case (previous daemon died, OS handed the same
|
|
15
|
+
* PID to an unrelated process before stale-timeout fired) would
|
|
16
|
+
* otherwise spuriously block startup. *
|
|
17
|
+
* Mode marker is enforced by the server-side factory
|
|
18
|
+
* (`createPtyHost`) because it polices in-process vs daemon ownership of
|
|
19
|
+
* the AppStateStore -- out of scope here.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
22
|
+
import lockfile from "proper-lockfile";
|
|
23
|
+
export class DaemonAlreadyRunningError extends Error {
|
|
24
|
+
pid;
|
|
25
|
+
pidFile;
|
|
26
|
+
constructor(pid, pidFile) {
|
|
27
|
+
super(`parasor-pty-host already running (pid=${pid}, lock=${pidFile})`);
|
|
28
|
+
this.pid = pid;
|
|
29
|
+
this.pidFile = pidFile;
|
|
30
|
+
this.name = "DaemonAlreadyRunningError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function acquireDaemonLock(paths, ourPid = process.pid) {
|
|
34
|
+
if (!existsSync(paths.lockFile)) {
|
|
35
|
+
writeFileSync(paths.lockFile, "");
|
|
36
|
+
}
|
|
37
|
+
let release;
|
|
38
|
+
try {
|
|
39
|
+
release = await lockfile.lock(paths.lockFile, {
|
|
40
|
+
stale: 60_000,
|
|
41
|
+
retries: 0,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
// ELOCKED is the only signal that another live daemon owns the
|
|
46
|
+
// lockfile. We surface the recorded pid (if alive) for diagnostics,
|
|
47
|
+
// but the rejection itself is decided entirely by flock -- the
|
|
48
|
+
// pidfile content cannot vote.
|
|
49
|
+
if (isLockHeldError(err)) {
|
|
50
|
+
const livePid = readPidIfAlive(paths.pidFile);
|
|
51
|
+
throw new DaemonAlreadyRunningError(livePid ?? -1, paths.pidFile);
|
|
52
|
+
}
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
// flock acquired: we own the runtime dir. Stamp our pid for `parasor
|
|
56
|
+
// pty-host status` consumers; do NOT recheck the pidfile, because a
|
|
57
|
+
// stale pid that has been recycled by an unrelated live process would
|
|
58
|
+
// otherwise block legitimate takeover.
|
|
59
|
+
writeFileSync(paths.pidFile, `${ourPid}\n`);
|
|
60
|
+
return {
|
|
61
|
+
pidFile: paths.pidFile,
|
|
62
|
+
release: async () => {
|
|
63
|
+
try {
|
|
64
|
+
if (existsSync(paths.pidFile)) {
|
|
65
|
+
const recorded = readPidStrict(paths.pidFile);
|
|
66
|
+
if (recorded === ourPid)
|
|
67
|
+
unlinkSync(paths.pidFile);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
/* best-effort cleanup */
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
await release();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
/* lockfile already released or removed */
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function readPidIfAlive(pidFile) {
|
|
83
|
+
if (!existsSync(pidFile))
|
|
84
|
+
return null;
|
|
85
|
+
let raw;
|
|
86
|
+
try {
|
|
87
|
+
raw = readFileSync(pidFile, "utf8").trim();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const pid = Number(raw);
|
|
93
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
94
|
+
return null;
|
|
95
|
+
try {
|
|
96
|
+
process.kill(pid, 0);
|
|
97
|
+
return pid;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function isLockHeldError(err) {
|
|
104
|
+
if (!(err instanceof Error))
|
|
105
|
+
return false;
|
|
106
|
+
const code = err.code;
|
|
107
|
+
if (code === "ELOCKED")
|
|
108
|
+
return true;
|
|
109
|
+
return /lock file is already being held/i.test(err.message);
|
|
110
|
+
}
|
|
111
|
+
function readPidStrict(pidFile) {
|
|
112
|
+
try {
|
|
113
|
+
const pid = Number(readFileSync(pidFile, "utf8").trim());
|
|
114
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=lockfile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../../src/pty/host-daemon/lockfile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export type AppStateMode = "in-process" | "daemon";
|
|
2
|
+
export interface ModeMarker {
|
|
3
|
+
mode: AppStateMode;
|
|
4
|
+
pid: number;
|
|
5
|
+
startedAt: string;
|
|
6
|
+
hostname: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class AppStateOwnerConflictError extends Error {
|
|
9
|
+
readonly currentMode: AppStateMode;
|
|
10
|
+
readonly existing: ModeMarker | null;
|
|
11
|
+
readonly markerFile: string;
|
|
12
|
+
constructor(currentMode: AppStateMode, existing: ModeMarker | null, markerFile: string);
|
|
13
|
+
}
|
|
14
|
+
export interface AppStateOwner {
|
|
15
|
+
marker: ModeMarker;
|
|
16
|
+
release: () => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Read and parse the marker file body. Returns null when the file does
|
|
20
|
+
* not exist or is malformed. Body is purely informational -- readers
|
|
21
|
+
* MUST NOT use it as a mutex; the proper-lockfile lock on the same
|
|
22
|
+
* path is what serialises ownership.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readMarker(markerFile: string): ModeMarker | null;
|
|
25
|
+
/**
|
|
26
|
+
* Write the marker body. Caller MUST already hold the proper-lockfile
|
|
27
|
+
* advisory lock on `markerFile` (see `acquireAppStateOwnership`).
|
|
28
|
+
*/
|
|
29
|
+
export declare function writeMarker(markerFile: string, mode: AppStateMode, pid?: number, startedAt?: string, hostname?: string): ModeMarker;
|
|
30
|
+
/** Best-effort delete; not an error if the file is already gone. */
|
|
31
|
+
export declare function unlinkMarker(markerFile: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Liveness probe via `process.kill(pid, 0)`. Used only to enrich the
|
|
34
|
+
* conflict error -- the proper-lockfile hold is the source of truth for
|
|
35
|
+
* whether the recorded owner is still alive.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isPidAlive(pid: number): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Conventional marker location: the AppState dir (i.e. the directory
|
|
40
|
+
* that holds `state.json`). Co-locating with state.json -- rather than
|
|
41
|
+
* the daemon runtime dir -- means any process that opens this state.json
|
|
42
|
+
* passes through the same marker file, which is the actual mutual-
|
|
43
|
+
* exclusion target. The daemon runtime dir varies with `XDG_RUNTIME_DIR`
|
|
44
|
+
* / `PARASOR_PTY_SOCK` overrides, so an in-process server with a custom
|
|
45
|
+
* `PARASOR_CONFIG_DIR` and a daemon with a custom `XDG_RUNTIME_DIR`
|
|
46
|
+
* could otherwise both write the same `state.json` undetected.
|
|
47
|
+
*/
|
|
48
|
+
export declare function markerFileFor(appStateDir: string): string;
|
|
49
|
+
interface AcquireOptions {
|
|
50
|
+
pid?: number;
|
|
51
|
+
startedAt?: string;
|
|
52
|
+
hostname?: string;
|
|
53
|
+
/** Test seam -- production callers always use proper-lockfile defaults. */
|
|
54
|
+
retries?: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Acquire exclusive ownership of the AppState directory. On success
|
|
58
|
+
* returns an `AppStateOwner` whose `release()` MUST be called from the
|
|
59
|
+
* graceful-shutdown path. The lock is held until release; long-running
|
|
60
|
+
* owners are kept alive by proper-lockfile's mtime refresh interval
|
|
61
|
+
* (default `stale/2 = 30s`).
|
|
62
|
+
*
|
|
63
|
+
* `proper-lockfile.lock()` is the *sole* mutex (mirrors
|
|
64
|
+
* `host-daemon/lockfile.ts`'s decision for the daemon runtime lock).
|
|
65
|
+
* The marker file body is purely informational -- populated for
|
|
66
|
+
* `AppStateOwnerConflictError` diagnostics, NOT cross-validated post-
|
|
67
|
+
* acquisition. a post-flock `kill(pid, 0)` recheck
|
|
68
|
+
* spuriously rejects legitimate stale takeover when the OS has
|
|
69
|
+
* recycled the prior owner's PID to an unrelated live process during
|
|
70
|
+
* the 60s stale window. The daemon lockfile path documents the same
|
|
71
|
+
* choice ("flock acquired: we own the runtime dir … do NOT recheck …
|
|
72
|
+
* because a stale pid that has been recycled by an unrelated live
|
|
73
|
+
* process would otherwise block legitimate takeover").
|
|
74
|
+
*/
|
|
75
|
+
export declare function acquireAppStateOwnership(markerFile: string, currentMode: AppStateMode, opts?: AcquireOptions): Promise<AppStateOwner>;
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=mode-marker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode-marker.d.ts","sourceRoot":"","sources":["../../../src/pty/host-daemon/mode-marker.ts"],"names":[],"mappings":"AA+BA,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEnD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,QAAQ,CAAC,WAAW,EAAE,YAAY;IAClC,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM;gBAFlB,WAAW,EAAE,YAAY,EACzB,QAAQ,EAAE,UAAU,GAAG,IAAI,EAC3B,UAAU,EAAE,MAAM;CAY9B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAkBhE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,YAAY,EAClB,GAAG,GAAE,MAAoB,EACzB,SAAS,GAAE,MAAiC,EAC5C,QAAQ,GAAE,MAAqB,GAC9B,UAAU,CAIZ;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAMrD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAc/C;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,YAAY,EACzB,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,aAAa,CAAC,CAiFxB"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* -- cross-mode mutual exclusion ownership lock.
|
|
3
|
+
*
|
|
4
|
+
* Both runtime modes (in-process server / daemon) write to the same
|
|
5
|
+
* AppStateStore on disk. If both run concurrently against the same
|
|
6
|
+
* runtime dir they corrupt each other's view. This module acquires an
|
|
7
|
+
* exclusive `proper-lockfile` advisory lock on a marker file next to
|
|
8
|
+
* `state.json` and holds it for the lifetime of the owning process.
|
|
9
|
+
*
|
|
10
|
+
* Design parallels `host-daemon/lockfile.ts`:
|
|
11
|
+
* - `proper-lockfile` is the *sole* mutex (kernel-backed via
|
|
12
|
+
* graceful-fs's atomic mkdir of `<marker>.lock`/). Held across the
|
|
13
|
+
* owner's whole lifetime; released via `AppStateOwner.release()` on
|
|
14
|
+
* graceful shutdown or by stale-timeout (60s) if the holder dies.
|
|
15
|
+
* - The marker file body is purely informational -- humans (and a
|
|
16
|
+
* future `parasor doctor`) read it to see who owns the dir; the
|
|
17
|
+
* conflict error pretty-prints those fields. Body content cannot
|
|
18
|
+
* vote against the lockfile decision.
|
|
19
|
+
* - PID liveness (`process.kill(pid, 0)`) is consulted only to
|
|
20
|
+
* populate `AppStateOwnerConflictError` for nicer messages.
|
|
21
|
+
*
|
|
22
|
+
* Wire format: a single TSV line `mode\tpid\tstartedAt\thostname`. TSV
|
|
23
|
+
* keeps the file shell-readable during incident response (`cat
|
|
24
|
+
* appstate.mode`). The parser rejects lines that don't match the
|
|
25
|
+
* 4-field shape.
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
28
|
+
import { hostname as osHostname } from "node:os";
|
|
29
|
+
import lockfile from "proper-lockfile";
|
|
30
|
+
export class AppStateOwnerConflictError extends Error {
|
|
31
|
+
currentMode;
|
|
32
|
+
existing;
|
|
33
|
+
markerFile;
|
|
34
|
+
constructor(currentMode, existing, markerFile) {
|
|
35
|
+
const tail = existing !== null
|
|
36
|
+
? `${existing.mode} mode (pid=${existing.pid}, host=${existing.hostname}, started=${existing.startedAt})`
|
|
37
|
+
: "an unidentified owner (lockfile held but marker body unreadable)";
|
|
38
|
+
super(`appstate is owned by ${tail}; refusing to start ${currentMode} mode against the same runtime dir. ` +
|
|
39
|
+
`Marker file: ${markerFile}`);
|
|
40
|
+
this.currentMode = currentMode;
|
|
41
|
+
this.existing = existing;
|
|
42
|
+
this.markerFile = markerFile;
|
|
43
|
+
this.name = "AppStateOwnerConflictError";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Read and parse the marker file body. Returns null when the file does
|
|
48
|
+
* not exist or is malformed. Body is purely informational -- readers
|
|
49
|
+
* MUST NOT use it as a mutex; the proper-lockfile lock on the same
|
|
50
|
+
* path is what serialises ownership.
|
|
51
|
+
*/
|
|
52
|
+
export function readMarker(markerFile) {
|
|
53
|
+
if (!existsSync(markerFile))
|
|
54
|
+
return null;
|
|
55
|
+
let raw;
|
|
56
|
+
try {
|
|
57
|
+
raw = readFileSync(markerFile, "utf8");
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const line = raw.split(/\r?\n/)[0] ?? "";
|
|
63
|
+
const fields = line.split("\t");
|
|
64
|
+
if (fields.length !== 4)
|
|
65
|
+
return null;
|
|
66
|
+
const [mode, pidStr, startedAt, hostname] = fields;
|
|
67
|
+
if (mode !== "in-process" && mode !== "daemon")
|
|
68
|
+
return null;
|
|
69
|
+
const pid = Number(pidStr);
|
|
70
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
71
|
+
return null;
|
|
72
|
+
if (typeof startedAt !== "string" || startedAt.length === 0)
|
|
73
|
+
return null;
|
|
74
|
+
if (typeof hostname !== "string" || hostname.length === 0)
|
|
75
|
+
return null;
|
|
76
|
+
return { mode, pid, startedAt, hostname };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Write the marker body. Caller MUST already hold the proper-lockfile
|
|
80
|
+
* advisory lock on `markerFile` (see `acquireAppStateOwnership`).
|
|
81
|
+
*/
|
|
82
|
+
export function writeMarker(markerFile, mode, pid = process.pid, startedAt = new Date().toISOString(), hostname = osHostname()) {
|
|
83
|
+
const line = `${mode}\t${pid}\t${startedAt}\t${hostname}\n`;
|
|
84
|
+
writeFileSync(markerFile, line, { encoding: "utf8", mode: 0o600 });
|
|
85
|
+
return { mode, pid, startedAt, hostname };
|
|
86
|
+
}
|
|
87
|
+
/** Best-effort delete; not an error if the file is already gone. */
|
|
88
|
+
export function unlinkMarker(markerFile) {
|
|
89
|
+
try {
|
|
90
|
+
if (existsSync(markerFile))
|
|
91
|
+
unlinkSync(markerFile);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
/* leave-on-error */
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Liveness probe via `process.kill(pid, 0)`. Used only to enrich the
|
|
99
|
+
* conflict error -- the proper-lockfile hold is the source of truth for
|
|
100
|
+
* whether the recorded owner is still alive.
|
|
101
|
+
*/
|
|
102
|
+
export function isPidAlive(pid) {
|
|
103
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
104
|
+
return false;
|
|
105
|
+
try {
|
|
106
|
+
process.kill(pid, 0);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
/*
|
|
111
|
+
* EPERM = PID exists but belongs to another user. From this
|
|
112
|
+
* module's standpoint that is "alive" because the runtime dir is
|
|
113
|
+
* per-user -- another user's PID inside our home dir would be a
|
|
114
|
+
* permissions misconfiguration we should not paper over.
|
|
115
|
+
*/
|
|
116
|
+
return err.code === "EPERM";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Conventional marker location: the AppState dir (i.e. the directory
|
|
121
|
+
* that holds `state.json`). Co-locating with state.json -- rather than
|
|
122
|
+
* the daemon runtime dir -- means any process that opens this state.json
|
|
123
|
+
* passes through the same marker file, which is the actual mutual-
|
|
124
|
+
* exclusion target. The daemon runtime dir varies with `XDG_RUNTIME_DIR`
|
|
125
|
+
* / `PARASOR_PTY_SOCK` overrides, so an in-process server with a custom
|
|
126
|
+
* `PARASOR_CONFIG_DIR` and a daemon with a custom `XDG_RUNTIME_DIR`
|
|
127
|
+
* could otherwise both write the same `state.json` undetected.
|
|
128
|
+
*/
|
|
129
|
+
export function markerFileFor(appStateDir) {
|
|
130
|
+
return `${appStateDir}/appstate.mode`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Acquire exclusive ownership of the AppState directory. On success
|
|
134
|
+
* returns an `AppStateOwner` whose `release()` MUST be called from the
|
|
135
|
+
* graceful-shutdown path. The lock is held until release; long-running
|
|
136
|
+
* owners are kept alive by proper-lockfile's mtime refresh interval
|
|
137
|
+
* (default `stale/2 = 30s`).
|
|
138
|
+
*
|
|
139
|
+
* `proper-lockfile.lock()` is the *sole* mutex (mirrors
|
|
140
|
+
* `host-daemon/lockfile.ts`'s decision for the daemon runtime lock).
|
|
141
|
+
* The marker file body is purely informational -- populated for
|
|
142
|
+
* `AppStateOwnerConflictError` diagnostics, NOT cross-validated post-
|
|
143
|
+
* acquisition. a post-flock `kill(pid, 0)` recheck
|
|
144
|
+
* spuriously rejects legitimate stale takeover when the OS has
|
|
145
|
+
* recycled the prior owner's PID to an unrelated live process during
|
|
146
|
+
* the 60s stale window. The daemon lockfile path documents the same
|
|
147
|
+
* choice ("flock acquired: we own the runtime dir … do NOT recheck …
|
|
148
|
+
* because a stale pid that has been recycled by an unrelated live
|
|
149
|
+
* process would otherwise block legitimate takeover").
|
|
150
|
+
*/
|
|
151
|
+
export async function acquireAppStateOwnership(markerFile, currentMode, opts = {}) {
|
|
152
|
+
const ourPid = opts.pid ?? process.pid;
|
|
153
|
+
const startedAt = opts.startedAt ?? new Date().toISOString();
|
|
154
|
+
const hostname = opts.hostname ?? osHostname();
|
|
155
|
+
/*
|
|
156
|
+
* `proper-lockfile.lock()` resolves the file's realpath and stats it,
|
|
157
|
+
* so the marker file must already exist. Touch an empty file when
|
|
158
|
+
* none is present; the body is overwritten further down once we own
|
|
159
|
+
* the lock. Same pattern as `host-daemon/lockfile.ts`.
|
|
160
|
+
*/
|
|
161
|
+
if (!existsSync(markerFile)) {
|
|
162
|
+
writeFileSync(markerFile, "", { mode: 0o600 });
|
|
163
|
+
}
|
|
164
|
+
let release;
|
|
165
|
+
try {
|
|
166
|
+
release = await lockfile.lock(markerFile, {
|
|
167
|
+
stale: 60_000,
|
|
168
|
+
retries: opts.retries ?? 0,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
if (isLockHeldError(err)) {
|
|
173
|
+
const existing = readMarker(markerFile);
|
|
174
|
+
throw new AppStateOwnerConflictError(currentMode, existing, markerFile);
|
|
175
|
+
}
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
178
|
+
// Lock acquired: we own the AppState dir. Stamp the marker body for
|
|
179
|
+
// diagnostics (`parasor doctor`, incident response). Stale body
|
|
180
|
+
// content from a previous owner is overwritten unconditionally --
|
|
181
|
+
// proper-lockfile already decided that owner is gone.
|
|
182
|
+
const marker = writeMarker(markerFile, currentMode, ourPid, startedAt, hostname);
|
|
183
|
+
return {
|
|
184
|
+
marker,
|
|
185
|
+
release: async () => {
|
|
186
|
+
/*
|
|
187
|
+
* Owner-verified body unlink (reviewed for correctness): only delete the
|
|
188
|
+
* marker body if it still names *us*. A previous owner whose
|
|
189
|
+
* lock got stale-stolen could otherwise unlink the new owner's
|
|
190
|
+
* body during a delayed shutdown handler. Same pattern as the
|
|
191
|
+
* daemon pidfile cleanup in `host-daemon/lockfile.ts`.
|
|
192
|
+
*/
|
|
193
|
+
try {
|
|
194
|
+
const recorded = readMarker(markerFile);
|
|
195
|
+
if (recorded !== null &&
|
|
196
|
+
recorded.pid === ourPid &&
|
|
197
|
+
recorded.startedAt === startedAt) {
|
|
198
|
+
unlinkMarker(markerFile);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
/* best-effort body cleanup */
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
await release();
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
/*
|
|
209
|
+
* surface release failure to stderr so a
|
|
210
|
+
* leftover `<marker>.lock/` directory after a crashy shutdown
|
|
211
|
+
* is observable. proper-lockfile's stale-timeout (60s) makes
|
|
212
|
+
* the next boot self-recover, but the operator still wants to
|
|
213
|
+
* know.
|
|
214
|
+
*/
|
|
215
|
+
console.error(`[mode-marker] release error for ${markerFile}: ${err.message}`);
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function isLockHeldError(err) {
|
|
221
|
+
if (!(err instanceof Error))
|
|
222
|
+
return false;
|
|
223
|
+
const code = err.code;
|
|
224
|
+
if (code === "ELOCKED")
|
|
225
|
+
return true;
|
|
226
|
+
return /lock file is already being held/i.test(err.message);
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=mode-marker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode-marker.js","sourceRoot":"","sources":["../../../src/pty/host-daemon/mode-marker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { SessionRecord } from "@parasor/shared";
|
|
2
|
+
export type OrphanReconcileTransition = {
|
|
3
|
+
type: "kept";
|
|
4
|
+
id: string;
|
|
5
|
+
} | {
|
|
6
|
+
type: "orphaned";
|
|
7
|
+
id: string;
|
|
8
|
+
previousDaemonPid: number;
|
|
9
|
+
} | {
|
|
10
|
+
type: "lost";
|
|
11
|
+
id: string;
|
|
12
|
+
reason: "no-pid" | "dead-pid";
|
|
13
|
+
};
|
|
14
|
+
export interface OrphanReconcileResult {
|
|
15
|
+
/** Snapshots after reconciliation, in input order. */
|
|
16
|
+
records: SessionRecord[];
|
|
17
|
+
/** Per-id transition summary (helpful for logging / tests). */
|
|
18
|
+
transitions: OrphanReconcileTransition[];
|
|
19
|
+
}
|
|
20
|
+
export interface ReconcileOpts {
|
|
21
|
+
currentDaemonPid: number;
|
|
22
|
+
/**
|
|
23
|
+
* ISO8601 timestamp of *this* daemon's start. Combined with
|
|
24
|
+
* `currentDaemonPid` to form the writer-generation tuple -- a record
|
|
25
|
+
* with a matching pid but a stale `daemonStartedAt` is treated as
|
|
26
|
+
* orphaned, defending against PID recycling between daemon restarts
|
|
27
|
+
* (reviewed for correctness). Required for correctness; an undefined value
|
|
28
|
+
* would silently degrade to PID-only matching.
|
|
29
|
+
*/
|
|
30
|
+
currentDaemonStartedAt: string;
|
|
31
|
+
/** Liveness probe; defaults to `process.kill(pid, 0)`. Test seam. */
|
|
32
|
+
isPidAlive?: (pid: number) => boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Pure function -- no side effects. Caller is responsible for persisting
|
|
36
|
+
* the resulting array via `AppStateStore.internalMutate`.
|
|
37
|
+
*
|
|
38
|
+
* Already-terminal records (state in {exited,lost,orphaned}) are passed
|
|
39
|
+
* through unchanged. Only `state === "running"` records are evaluated.
|
|
40
|
+
*/
|
|
41
|
+
export declare function reconcileSessionRecords(input: readonly SessionRecord[], opts: ReconcileOpts): OrphanReconcileResult;
|
|
42
|
+
//# sourceMappingURL=orphan-cleanup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orphan-cleanup.d.ts","sourceRoot":"","sources":["../../../src/pty/host-daemon/orphan-cleanup.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,MAAM,yBAAyB,GACjC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAA;CAAE,CAAC;AAEhE,MAAM,WAAW,qBAAqB;IACpC,sDAAsD;IACtD,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,+DAA+D;IAC/D,WAAW,EAAE,yBAAyB,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qEAAqE;IACrE,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACvC;AAYD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,IAAI,EAAE,aAAa,GAClB,qBAAqB,CAyCvB"}
|