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