|
1 | 1 | import { randomUUID } from "node:crypto"; |
| 2 | +import { existsSync, realpathSync } from "node:fs"; |
2 | 3 | import { dirname, join, resolve } from "node:path"; |
3 | 4 | // Make sibling binaries (if any are added later) discoverable on $PATH so |
4 | 5 | // child processes spawned without an absolute path still find them. |
@@ -139,27 +140,51 @@ const waitForShutdownSignal = () => |
139 | 140 | const isRecord = (value: unknown): value is Record<string, unknown> => |
140 | 141 | typeof value === "object" && value !== null && !Array.isArray(value); |
141 | 142 |
|
142 | | -const isServerReachable = (baseUrl: string): Effect.Effect<boolean> => |
| 143 | +interface DaemonScopeInfo { |
| 144 | + readonly id: string; |
| 145 | + readonly name: string; |
| 146 | + readonly dir: string; |
| 147 | +} |
| 148 | + |
| 149 | +const readDaemonScopeInfo = (baseUrl: string): Effect.Effect<DaemonScopeInfo | null> => |
143 | 150 | Effect.tryPromise(() => |
144 | 151 | fetch(`${baseUrl}/api/scope`, { signal: AbortSignal.timeout(2000) }), |
145 | 152 | ).pipe( |
146 | 153 | Effect.flatMap((res) => { |
147 | | - if (!res.ok) return Effect.succeed(false); |
| 154 | + if (!res.ok) return Effect.succeed(null); |
148 | 155 | return Effect.tryPromise(() => res.json()).pipe( |
149 | 156 | Effect.map((payload) => { |
150 | | - if (!isRecord(payload)) return false; |
151 | | - return ( |
| 157 | + if (!isRecord(payload)) return null; |
| 158 | + if ( |
152 | 159 | typeof payload.id === "string" && |
153 | 160 | typeof payload.name === "string" && |
154 | 161 | typeof payload.dir === "string" |
155 | | - ); |
| 162 | + ) { |
| 163 | + return { |
| 164 | + id: payload.id, |
| 165 | + name: payload.name, |
| 166 | + dir: payload.dir, |
| 167 | + }; |
| 168 | + } |
| 169 | + return null; |
156 | 170 | }), |
157 | | - Effect.catchCause(() => Effect.succeed(false)), |
| 171 | + Effect.catchCause(() => Effect.succeed(null)), |
158 | 172 | ); |
159 | 173 | }), |
160 | | - Effect.catchCause(() => Effect.succeed(false)), |
| 174 | + Effect.catchCause(() => Effect.succeed(null)), |
161 | 175 | ); |
162 | 176 |
|
| 177 | +const isServerReachable = (baseUrl: string): Effect.Effect<boolean> => |
| 178 | + readDaemonScopeInfo(baseUrl).pipe(Effect.map((scopeInfo) => scopeInfo !== null)); |
| 179 | + |
| 180 | +const normalizeDaemonScopeDir = (dir: string): string => { |
| 181 | + const resolved = resolve(dir); |
| 182 | + return existsSync(resolved) ? realpathSync.native(resolved) : resolved; |
| 183 | +}; |
| 184 | + |
| 185 | +const currentDaemonScopeDir = (): string => |
| 186 | + normalizeDaemonScopeDir(process.env.EXECUTOR_SCOPE_DIR ?? process.cwd()); |
| 187 | + |
163 | 188 | const script = process.argv[1]; |
164 | 189 | const isDevMode = isDevCliEntrypoint(script); |
165 | 190 | const cliPrefix = isDevMode ? `bun run ${script}` : "executor"; |
@@ -302,7 +327,8 @@ const ensureDaemon = ( |
302 | 327 | ): Effect.Effect<string, Error, FileSystem.FileSystem | PlatformPath.Path> => |
303 | 328 | Effect.gen(function* () { |
304 | 329 | const resolvedTarget = yield* resolveDaemonTarget(baseUrl); |
305 | | - if (yield* isServerReachable(resolvedTarget.baseUrl)) { |
| 330 | + const reachableScope = yield* readDaemonScopeInfo(resolvedTarget.baseUrl); |
| 331 | + if (reachableScope && normalizeDaemonScopeDir(reachableScope.dir) === currentDaemonScopeDir()) { |
306 | 332 | return resolvedTarget.baseUrl; |
307 | 333 | } |
308 | 334 |
|
|
0 commit comments