( baseUrl: string, )
| 852 | }).pipe(Effect.mapError(toError)); |
| 853 | |
| 854 | const stopDaemon = ( |
| 855 | baseUrl: string, |
| 856 | ): Effect.Effect<void, Error, FileSystem.FileSystem | PlatformPath.Path> => |
| 857 | Effect.gen(function* () { |
| 858 | const target = yield* resolveDaemonTarget(baseUrl); |
| 859 | const host = canonicalDaemonHost(target.hostname); |
| 860 | const scopeId = target.scopeId; |
| 861 | const record = yield* readDaemonRecord({ hostname: host, port: target.port }); |
| 862 | const reachable = yield* isServerReachable(target.baseUrl); |
| 863 | |
| 864 | if (!record) { |
| 865 | if (reachable) { |
| 866 | return yield* Effect.fail( |
| 867 | new Error( |
| 868 | [ |
| 869 | `Executor is reachable at ${target.baseUrl} but no daemon record exists.`, |
| 870 | "It may not be managed by this CLI process.", |
| 871 | "Stop it from the terminal/session where it was started.", |
| 872 | ].join("\n"), |
| 873 | ), |
| 874 | ); |
| 875 | } |
| 876 | console.log(`No daemon running at ${target.baseUrl}.`); |
| 877 | return; |
| 878 | } |
| 879 | |
| 880 | if (!isPidAlive(record.pid)) { |
| 881 | yield* removeDaemonRecord({ hostname: host, port: target.port }); |
| 882 | yield* removeDaemonPointer({ hostname: host, scopeId }).pipe(Effect.ignore); |
| 883 | if (reachable) { |
| 884 | return yield* Effect.fail( |
| 885 | new Error( |
| 886 | [ |
| 887 | `Daemon record for ${target.baseUrl} points to dead pid ${record.pid}, but endpoint is still reachable.`, |
| 888 | "Refusing to stop an unknown process without ownership metadata.", |
| 889 | ].join("\n"), |
| 890 | ), |
| 891 | ); |
| 892 | } |
| 893 | console.log( |
| 894 | `No daemon running at ${target.baseUrl} (removed stale record for pid ${record.pid}).`, |
| 895 | ); |
| 896 | return; |
| 897 | } |
| 898 | |
| 899 | console.log(`Stopping daemon at ${target.baseUrl} (pid ${record.pid})...`); |
| 900 | |
| 901 | yield* terminatePid(record.pid); |
| 902 | |
| 903 | const stopped = yield* waitForUnreachable({ |
| 904 | check: isServerReachable(target.baseUrl), |
| 905 | timeoutMs: DAEMON_STOP_TIMEOUT_MS, |
| 906 | intervalMs: DAEMON_BOOT_POLL_MS, |
| 907 | }); |
| 908 | |
| 909 | if (!stopped) { |
| 910 | return yield* Effect.fail( |
| 911 | new Error( |
no test coverage detected