#!/usr/bin/env node import { homedir } from "node:os"; import * as NodeRuntime from "@effect/platform-node/NodeRuntime";import * as NodeServices from "@effect/platform-node/NodeServices";import { NetService } from "@t3tools/shared/Net";import { Config, Data, Effect, Hash, Layer, Logger, Option, Path, Schema } from "effect";import { Argument, Command, Flag } from "effect/unstable/cli";import { ChildProcess } from "effect/unstable/process"; const BASE_SERVER_PORT = 3773;const BASE_WEB_PORT = 5733;const MAX_HASH_OFFSET = 3000;const MAX_PORT = 65535; export const DEFAULT_DEV_STATE_DIR = Effect.map(Effect.service(Path.Path), (path) => path.join(homedir(), ".t3", "dev"),); const MODE_ARGS = { dev: [ "run", "dev", "--ui=tui", "--filter=@t3tools/contracts", "--filter=@t3tools/web", "--filter=t3", "--parallel", ], "dev:server": ["run", "dev", "--filter=t3"], "dev:web": ["run", "dev", "--filter=@t3tools/web"], "dev:desktop": ["run", "dev", "--filter=@t3tools/desktop", "--filter=@t3tools/web", "--parallel"],} as const satisfies Record<string, ReadonlyArray<string>>; type DevMode = keyof typeof MODE_ARGS;type PortAvailabilityCheck<R = never> = (port: number) => Effect.Effect<boolean, never, R>; const DEV_RUNNER_MODES = Object.keys(MODE_ARGS) as Array<DevMode>; class DevRunnerError extends Data.TaggedError("DevRunnerError")<{ readonly message: string; readonly cause?: unknown;}> {} const optionalStringConfig = (name: string): Config.Config<string | undefined> => Config.string(name).pipe( Config.option, Config.map((value) => Option.getOrUndefined(value)), );const optionalBooleanConfig = (name: string): Config.Config<boolean | undefined> => Config.boolean(name).pipe( Config.option, Config.map((value) => Option.getOrUndefined(value)), );const optionalPortConfig = (name: string): Config.Config<number | undefined> => Config.port(name).pipe( Config.option, Config.map((value) => Option.getOrUndefined(value)), );const optionalIntegerConfig = (name: string): Config.Config<number | undefined> => Config.int(name).pipe( Config.option, Config.map((value) => Option.getOrUndefined(value)), );const optionalUrlConfig = (name: string): Config.Config<URL | undefined> => Config.url(name).pipe( Config.option, Config.map((value) => Option.getOrUndefined(value)), ); const OffsetConfig = Config.all({ portOffset: optionalIntegerConfig("T3CODE_PORT_OFFSET"), devInstance: optionalStringConfig("T3CODE_DEV_INSTANCE"),}); export function resolveOffset(config: { readonly portOffset: number | undefined; readonly devInstance: string | undefined;}): { readonly offset: number; readonly source: string } { if (config.portOffset !== undefined) { if (config.portOffset < 0) { throw new Error(`Invalid T3CODE_PORT_OFFSET: ${config.portOffset}`); } return { offset: config.portOffset, source: `T3CODE_PORT_OFFSET=${config.portOffset}`, }; } const seed = config.devInstance?.trim(); if (!seed) { return { offset: 0, source: "default ports" }; } if (/^\d+$/.test(seed)) { return { offset: Number(seed), source: `numeric T3CODE_DEV_INSTANCE=${seed}` }; } const offset = ((Hash.string(seed) >>> 0) % MAX_HASH_OFFSET) + 1; return { offset, source: `hashed T3CODE_DEV_INSTANCE=${seed}` };} function resolveStateDir(stateDir: string | undefined): Effect.Effect<string, never, Path.Path> { return Effect.gen(function* () { const path = yield* Path.Path; const configured = stateDir?.trim(); if (configured) { // Resolve relative paths against cwd (monorepo root) before turbo changes directories. return path.resolve(configured); } return yield* DEFAULT_DEV_STATE_DIR; });} interface CreateDevRunnerEnvInput { readonly mode: DevMode; readonly baseEnv: NodeJS.ProcessEnv; readonly serverOffset: number; readonly webOffset: number; readonly stateDir: string | undefined; readonly authToken: string | undefined; readonly noBrowser: boolean | undefined; readonly autoBootstrapProjectFromCwd: boolean | undefined; readonly logWebSocketEvents: boolean | undefined; readonly host: string | undefined; readonly port: number | undefined; readonly devUrl: URL | undefined;} export function createDevRunnerEnv({ mode, baseEnv, serverOffset, webOffset, stateDir, authToken, noBrowser, autoBootstrapProjectFromCwd, logWebSocketEvents, host, port, devUrl,}: CreateDevRunnerEnvInput): Effect.Effect<NodeJS.ProcessEnv, never, Path.Path> { return Effect.gen(function* () { const serverPort = port ?? BASE_SERVER_PORT + serverOffset; const webPort = BASE_WEB_PORT + webOffset; const resolvedStateDir = yield* resolveStateDir(stateDir); const output: NodeJS.ProcessEnv = { ...baseEnv, T3CODE_PORT: String(serverPort), PORT: String(webPort), ELECTRON_RENDERER_PORT: String(webPort), VITE_WS_URL: `ws://localhost:${serverPort}`, VITE_DEV_SERVER_URL: devUrl?.toString() ?? `http://localhost:${webPort}`, T3CODE_STATE_DIR: resolvedStateDir, }; if (host !== undefined) { output.T3CODE_HOST = host; } if (authToken !== undefined) { output.T3CODE_AUTH_TOKEN = authToken; } else { delete output.T3CODE_AUTH_TOKEN; } if (noBrowser !== undefined) { output.T3CODE_NO_BROWSER = noBrowser ? "1" : "0"; } else { delete output.T3CODE_NO_BROWSER; } if (autoBootstrapProjectFromCwd !== undefined) { output.T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD = autoBootstrapProjectFromCwd ? "1" : "0"; } else { delete output.T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD; } if (logWebSocketEvents !== undefined) { output.T3CODE_LOG_WS_EVENTS = logWebSocketEvents ? "1" : "0"; } else { delete output.T3CODE_LOG_WS_EVENTS; } if (mode === "dev") { output.T3CODE_MODE = "web"; delete output.T3CODE_DESKTOP_WS_URL; } if (mode === "dev:server" || mode === "dev:web") { output.T3CODE_MODE = "web"; delete output.T3CODE_DESKTOP_WS_URL; } return output; });} function portPairForOffset(offset: number): { readonly serverPort: number; readonly webPort: number;} { return { serverPort: BASE_SERVER_PORT + offset, webPort: BASE_WEB_PORT + offset, };} const defaultCheckPortAvailability: PortAvailabilityCheck<NetService> = (port) => Effect.gen(function* () { const net = yield* NetService; return yield* net.isPortAvailableOnLoopback(port); }); interface FindFirstAvailableOffsetInput<R = NetService> { readonly startOffset: number; readonly requireServerPort: boolean; readonly requireWebPort: boolean; readonly checkPortAvailability?: PortAvailabilityCheck<R>;} export function findFirstAvailableOffset<R = NetService>({ startOffset, requireServerPort, requireWebPort, checkPortAvailability,}: FindFirstAvailableOffsetInput<R>): Effect.Effect<number, DevRunnerError, R> { return Effect.gen(function* () { const checkPort = (checkPortAvailability ?? defaultCheckPortAvailability) as PortAvailabilityCheck<R>; for (let candidate = startOffset; ; candidate += 1) { const { serverPort, webPort } = portPairForOffset(candidate); const serverPortOutOfRange = serverPort > MAX_PORT; const webPortOutOfRange = webPort > MAX_PORT; if ( (requireServerPort && serverPortOutOfRange) || (requireWebPort && webPortOutOfRange) || (!requireServerPort && !requireWebPort && (serverPortOutOfRange || webPortOutOfRange)) ) { break; } const checks: Array<Effect.Effect<boolean, never, R>> = []; if (requireServerPort) { checks.push(checkPort(serverPort)); } if (requireWebPort) { checks.push(checkPort(webPort)); } if (checks.length === 0) { return candidate; } const availability = yield* Effect.all(checks); if (availability.every(Boolean)) { return candidate; } } return yield* new DevRunnerError({ message: `No available dev ports found from offset ${startOffset}. Tried server=${BASE_SERVER_PORT}+n web=${BASE_WEB_PORT}+n up to port ${MAX_PORT}.`, }); });} interface ResolveModePortOffsetsInput<R = NetService> { readonly mode: DevMode; readonly startOffset: number; readonly hasExplicitServerPort: boolean; readonly hasExplicitDevUrl: boolean; readonly checkPortAvailability?: PortAvailabilityCheck<R>;} export function resolveModePortOffsets<R = NetService>({ mode, startOffset, hasExplicitServerPort, hasExplicitDevUrl, checkPortAvailability,}: ResolveModePortOffsetsInput<R>): Effect.Effect< { readonly serverOffset: number; readonly webOffset: number }, DevRunnerError, R> { return Effect.gen(function* () { const checkPort = (checkPortAvailability ?? defaultCheckPortAvailability) as PortAvailabilityCheck<R>; if (mode === "dev:web") { if (hasExplicitDevUrl) { return { serverOffset: startOffset, webOffset: startOffset }; } const webOffset = yield* findFirstAvailableOffset({ startOffset, requireServerPort: false, requireWebPort: true, checkPortAvailability: checkPort, }); return { serverOffset: startOffset, webOffset }; } if (mode === "dev:server") { if (hasExplicitServerPort) { return { serverOffset: startOffset, webOffset: startOffset }; } const serverOffset = yield* findFirstAvailableOffset({ startOffset, requireServerPort: true, requireWebPort: false, checkPortAvailability: checkPort, }); return { serverOffset, webOffset: serverOffset }; } const sharedOffset = yield* findFirstAvailableOffset({ startOffset, requireServerPort: !hasExplicitServerPort, requireWebPort: !hasExplicitDevUrl, checkPortAvailability: checkPort, }); return { serverOffset: sharedOffset, webOffset: sharedOffset }; });} interface DevRunnerCliInput { readonly mode: DevMode; readonly stateDir: string | undefined; readonly authToken: string | undefined; readonly noBrowser: boolean | undefined; readonly autoBootstrapProjectFromCwd: boolean | undefined; readonly logWebSocketEvents: boolean | undefined; readonly host: string | undefined; readonly port: number | undefined; readonly devUrl: URL | undefined; readonly dryRun: boolean; readonly turboArgs: ReadonlyArray<string>;} const readOptionalBooleanEnv = (name: string): boolean | undefined => { const value = process.env[name]; if (value === undefined) { return undefined; } if (value === "1" || value.toLowerCase() === "true") { return true; } if (value === "0" || value.toLowerCase() === "false") { return false; } return undefined;}; const resolveOptionalBooleanOverride = ( explicitValue: boolean | undefined, envValue: boolean | undefined,): boolean | undefined => { if (explicitValue === true) { return true; } if (explicitValue === false) { return envValue; } return envValue;}; export function runDevRunnerWithInput(input: DevRunnerCliInput) { return Effect.gen(function* () { const { portOffset, devInstance } = yield* OffsetConfig.asEffect().pipe( Effect.mapError( (cause) => new DevRunnerError({ message: "Failed to read T3CODE_PORT_OFFSET/T3CODE_DEV_INSTANCE configuration.", cause, }), ), ); const { offset, source } = yield* Effect.try({ try: () => resolveOffset({ portOffset, devInstance }), catch: (cause) => new DevRunnerError({ message: cause instanceof Error ? cause.message : String(cause), cause, }), }); const envOverrides = { noBrowser: readOptionalBooleanEnv("T3CODE_NO_BROWSER"), autoBootstrapProjectFromCwd: readOptionalBooleanEnv( "T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD", ), logWebSocketEvents: readOptionalBooleanEnv("T3CODE_LOG_WS_EVENTS"), }; const { serverOffset, webOffset } = yield* resolveModePortOffsets({ mode: input.mode, startOffset: offset, hasExplicitServerPort: input.port !== undefined, hasExplicitDevUrl: input.devUrl !== undefined, }); const env = yield* createDevRunnerEnv({ mode: input.mode, baseEnv: process.env, serverOffset, webOffset, stateDir: input.stateDir, authToken: input.authToken, noBrowser: resolveOptionalBooleanOverride(input.noBrowser, envOverrides.noBrowser), autoBootstrapProjectFromCwd: resolveOptionalBooleanOverride( input.autoBootstrapProjectFromCwd, envOverrides.autoBootstrapProjectFromCwd, ), logWebSocketEvents: resolveOptionalBooleanOverride( input.logWebSocketEvents, envOverrides.logWebSocketEvents, ), host: input.host, port: input.port, devUrl: input.devUrl, }); const selectionSuffix = serverOffset !== offset || webOffset !== offset ? ` selectedOffset(server=${serverOffset},web=${webOffset})` : ""; yield* Effect.logInfo( `[dev-runner] mode=${input.mode} source=${source}${selectionSuffix} serverPort=${String(env.T3CODE_PORT)} webPort=${String(env.PORT)} stateDir=${String(env.T3CODE_STATE_DIR)}`, ); if (input.dryRun) { return; } const child = yield* ChildProcess.make( "turbo", [...MODE_ARGS[input.mode], ...input.turboArgs], { stdin: "inherit", stdout: "inherit", stderr: "inherit", env, extendEnv: false, }, ); const exitCode = yield* child.exitCode; if (exitCode !== 0) { return yield* new DevRunnerError({ message: `turbo exited with code ${exitCode}`, }); } }).pipe( Effect.mapError((cause) => cause instanceof DevRunnerError ? cause : new DevRunnerError({ message: cause instanceof Error ? cause.message : "dev-runner failed", cause, }), ), );} const devRunnerCli = Command.make("dev-runner", { mode: Argument.choice("mode", DEV_RUNNER_MODES).pipe( Argument.withDescription("Development mode to run."), ), stateDir: Flag.string("state-dir").pipe( Flag.withDescription("State directory path (forwards to T3CODE_STATE_DIR)."), Flag.withFallbackConfig(optionalStringConfig("T3CODE_STATE_DIR")), ), authToken: Flag.string("auth-token").pipe( Flag.withDescription("Auth token (forwards to T3CODE_AUTH_TOKEN)."), Flag.withAlias("token"), Flag.withFallbackConfig(optionalStringConfig("T3CODE_AUTH_TOKEN")), ), noBrowser: Flag.boolean("no-browser").pipe( Flag.withDescription("Browser auto-open toggle (equivalent to T3CODE_NO_BROWSER)."), Flag.withFallbackConfig(optionalBooleanConfig("T3CODE_NO_BROWSER")), ), autoBootstrapProjectFromCwd: Flag.boolean("auto-bootstrap-project-from-cwd").pipe( Flag.withDescription( "Auto-bootstrap toggle (equivalent to T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD).", ), Flag.withFallbackConfig(optionalBooleanConfig("T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD")),Lines 1–500 of 547. Load more
#!/usr/bin/env node import { homedir } from "node:os"; import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { NetService } from "@t3tools/shared/Net"; import { Config, Data, Effect, Hash, Layer, Logger, Option, Path, Schema } from "effect"; import { Argument, Command, Flag } from "effect/unstable/cli"; import { ChildProcess } from "effect/unstable/process"; const BASE_SERVER_PORT = 3773; const BASE_WEB_PORT = 5733; const MAX_HASH_OFFSET = 3000; const MAX_PORT = 65535; export const DEFAULT_DEV_STATE_DIR = Effect.map(Effect.service(Path.Path), (path) => path.join(homedir(), ".t3", "dev"), ); const MODE_ARGS = { dev: [ "run", "dev", "--ui=tui", "--filter=@t3tools/contracts", "--filter=@t3tools/web", "--filter=t3", "--parallel", ], "dev:server": ["run", "dev", "--filter=t3"], "dev:web": ["run", "dev", "--filter=@t3tools/web"], "dev:desktop": ["run", "dev", "--filter=@t3tools/desktop", "--filter=@t3tools/web", "--parallel"], } as const satisfies Record<string, ReadonlyArray<string>>; type DevMode = keyof typeof MODE_ARGS; type PortAvailabilityCheck<R = never> = (port: number) => Effect.Effect<boolean, never, R>; const DEV_RUNNER_MODES = Object.keys(MODE_ARGS) as Array<DevMode>; class DevRunnerError extends Data.TaggedError("DevRunnerError")<{ readonly message: string; readonly cause?: unknown; }> {} const optionalStringConfig = (name: string): Config.Config<string | undefined> => Config.string(name).pipe( Config.option, Config.map((value) => Option.getOrUndefined(value)), ); const optionalBooleanConfig = (name: string): Config.Config<boolean | undefined> => Config.boolean(name).pipe( Config.option, Config.map((value) => Option.getOrUndefined(value)), ); const optionalPortConfig = (name: string): Config.Config<number | undefined> => Config.port(name).pipe( Config.option, Config.map((value) => Option.getOrUndefined(value)), ); const optionalIntegerConfig = (name: string): Config.Config<number | undefined> => Config.int(name).pipe( Config.option, Config.map((value) => Option.getOrUndefined(value)), ); const optionalUrlConfig = (name: string): Config.Config<URL | undefined> => Config.url(name).pipe( Config.option, Config.map((value) => Option.getOrUndefined(value)), ); const OffsetConfig = Config.all({ portOffset: optionalIntegerConfig("T3CODE_PORT_OFFSET"), devInstance: optionalStringConfig("T3CODE_DEV_INSTANCE"), }); export function resolveOffset(config: { readonly portOffset: number | undefined; readonly devInstance: string | undefined; }): { readonly offset: number; readonly source: string } { if (config.portOffset !== undefined) { if (config.portOffset < 0) { throw new Error(`Invalid T3CODE_PORT_OFFSET: ${config.portOffset}`); } return { offset: config.portOffset, source: `T3CODE_PORT_OFFSET=${config.portOffset}`, }; } const seed = config.devInstance?.trim(); if (!seed) { return { offset: 0, source: "default ports" }; } if (/^\d+$/.test(seed)) { return { offset: Number(seed), source: `numeric T3CODE_DEV_INSTANCE=${seed}` }; } const offset = ((Hash.string(seed) >>> 0) % MAX_HASH_OFFSET) + 1; return { offset, source: `hashed T3CODE_DEV_INSTANCE=${seed}` }; } function resolveStateDir(stateDir: string | undefined): Effect.Effect<string, never, Path.Path> { return Effect.gen(function* () { const path = yield* Path.Path; const configured = stateDir?.trim(); if (configured) { // Resolve relative paths against cwd (monorepo root) before turbo changes directories. return path.resolve(configured); } return yield* DEFAULT_DEV_STATE_DIR; }); } interface CreateDevRunnerEnvInput { readonly mode: DevMode; readonly baseEnv: NodeJS.ProcessEnv; readonly serverOffset: number; readonly webOffset: number; readonly stateDir: string | undefined; readonly authToken: string | undefined; readonly noBrowser: boolean | undefined; readonly autoBootstrapProjectFromCwd: boolean | undefined; readonly logWebSocketEvents: boolean | undefined; readonly host: string | undefined; readonly port: number | undefined; readonly devUrl: URL | undefined; } export function createDevRunnerEnv({ mode, baseEnv, serverOffset, webOffset, stateDir, authToken, noBrowser, autoBootstrapProjectFromCwd, logWebSocketEvents, host, port, devUrl, }: CreateDevRunnerEnvInput): Effect.Effect<NodeJS.ProcessEnv, never, Path.Path> { return Effect.gen(function* () { const serverPort = port ?? BASE_SERVER_PORT + serverOffset; const webPort = BASE_WEB_PORT + webOffset; const resolvedStateDir = yield* resolveStateDir(stateDir); const output: NodeJS.ProcessEnv = { ...baseEnv, T3CODE_PORT: String(serverPort), PORT: String(webPort), ELECTRON_RENDERER_PORT: String(webPort), VITE_WS_URL: `ws://localhost:${serverPort}`, VITE_DEV_SERVER_URL: devUrl?.toString() ?? `http://localhost:${webPort}`, T3CODE_STATE_DIR: resolvedStateDir, }; if (host !== undefined) { output.T3CODE_HOST = host; } if (authToken !== undefined) { output.T3CODE_AUTH_TOKEN = authToken; } else { delete output.T3CODE_AUTH_TOKEN; } if (noBrowser !== undefined) { output.T3CODE_NO_BROWSER = noBrowser ? "1" : "0"; } else { delete output.T3CODE_NO_BROWSER; } if (autoBootstrapProjectFromCwd !== undefined) { output.T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD = autoBootstrapProjectFromCwd ? "1" : "0"; } else { delete output.T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD; } if (logWebSocketEvents !== undefined) { output.T3CODE_LOG_WS_EVENTS = logWebSocketEvents ? "1" : "0"; } else { delete output.T3CODE_LOG_WS_EVENTS; } if (mode === "dev") { output.T3CODE_MODE = "web"; delete output.T3CODE_DESKTOP_WS_URL; } if (mode === "dev:server" || mode === "dev:web") { output.T3CODE_MODE = "web"; delete output.T3CODE_DESKTOP_WS_URL; } return output; }); } function portPairForOffset(offset: number): { readonly serverPort: number; readonly webPort: number; } { return { serverPort: BASE_SERVER_PORT + offset, webPort: BASE_WEB_PORT + offset, }; } const defaultCheckPortAvailability: PortAvailabilityCheck<NetService> = (port) => Effect.gen(function* () { const net = yield* NetService; return yield* net.isPortAvailableOnLoopback(port); }); interface FindFirstAvailableOffsetInput<R = NetService> { readonly startOffset: number; readonly requireServerPort: boolean; readonly requireWebPort: boolean; readonly checkPortAvailability?: PortAvailabilityCheck<R>; } export function findFirstAvailableOffset<R = NetService>({ startOffset, requireServerPort, requireWebPort, checkPortAvailability, }: FindFirstAvailableOffsetInput<R>): Effect.Effect<number, DevRunnerError, R> { return Effect.gen(function* () { const checkPort = (checkPortAvailability ?? defaultCheckPortAvailability) as PortAvailabilityCheck<R>; for (let candidate = startOffset; ; candidate += 1) { const { serverPort, webPort } = portPairForOffset(candidate); const serverPortOutOfRange = serverPort > MAX_PORT; const webPortOutOfRange = webPort > MAX_PORT; if ( (requireServerPort && serverPortOutOfRange) || (requireWebPort && webPortOutOfRange) || (!requireServerPort && !requireWebPort && (serverPortOutOfRange || webPortOutOfRange)) ) { break; } const checks: Array<Effect.Effect<boolean, never, R>> = []; if (requireServerPort) { checks.push(checkPort(serverPort)); } if (requireWebPort) { checks.push(checkPort(webPort)); } if (checks.length === 0) { return candidate; } const availability = yield* Effect.all(checks); if (availability.every(Boolean)) { return candidate; } } return yield* new DevRunnerError({ message: `No available dev ports found from offset ${startOffset}. Tried server=${BASE_SERVER_PORT}+n web=${BASE_WEB_PORT}+n up to port ${MAX_PORT}.`, }); }); } interface ResolveModePortOffsetsInput<R = NetService> { readonly mode: DevMode; readonly startOffset: number; readonly hasExplicitServerPort: boolean; readonly hasExplicitDevUrl: boolean; readonly checkPortAvailability?: PortAvailabilityCheck<R>; } export function resolveModePortOffsets<R = NetService>({ mode, startOffset, hasExplicitServerPort, hasExplicitDevUrl, checkPortAvailability, }: ResolveModePortOffsetsInput<R>): Effect.Effect< { readonly serverOffset: number; readonly webOffset: number }, DevRunnerError, R > { return Effect.gen(function* () { const checkPort = (checkPortAvailability ?? defaultCheckPortAvailability) as PortAvailabilityCheck<R>; if (mode === "dev:web") { if (hasExplicitDevUrl) { return { serverOffset: startOffset, webOffset: startOffset }; } const webOffset = yield* findFirstAvailableOffset({ startOffset, requireServerPort: false, requireWebPort: true, checkPortAvailability: checkPort, }); return { serverOffset: startOffset, webOffset }; } if (mode === "dev:server") { if (hasExplicitServerPort) { return { serverOffset: startOffset, webOffset: startOffset }; } const serverOffset = yield* findFirstAvailableOffset({ startOffset, requireServerPort: true, requireWebPort: false, checkPortAvailability: checkPort, }); return { serverOffset, webOffset: serverOffset }; } const sharedOffset = yield* findFirstAvailableOffset({ startOffset, requireServerPort: !hasExplicitServerPort, requireWebPort: !hasExplicitDevUrl, checkPortAvailability: checkPort, }); return { serverOffset: sharedOffset, webOffset: sharedOffset }; }); } interface DevRunnerCliInput { readonly mode: DevMode; readonly stateDir: string | undefined; readonly authToken: string | undefined; readonly noBrowser: boolean | undefined; readonly autoBootstrapProjectFromCwd: boolean | undefined; readonly logWebSocketEvents: boolean | undefined; readonly host: string | undefined; readonly port: number | undefined; readonly devUrl: URL | undefined; readonly dryRun: boolean; readonly turboArgs: ReadonlyArray<string>; } const readOptionalBooleanEnv = (name: string): boolean | undefined => { const value = process.env[name]; if (value === undefined) { return undefined; } if (value === "1" || value.toLowerCase() === "true") { return true; } if (value === "0" || value.toLowerCase() === "false") { return false; } return undefined; }; const resolveOptionalBooleanOverride = ( explicitValue: boolean | undefined, envValue: boolean | undefined, ): boolean | undefined => { if (explicitValue === true) { return true; } if (explicitValue === false) { return envValue; } return envValue; }; export function runDevRunnerWithInput(input: DevRunnerCliInput) { return Effect.gen(function* () { const { portOffset, devInstance } = yield* OffsetConfig.asEffect().pipe( Effect.mapError( (cause) => new DevRunnerError({ message: "Failed to read T3CODE_PORT_OFFSET/T3CODE_DEV_INSTANCE configuration.", cause, }), ), ); const { offset, source } = yield* Effect.try({ try: () => resolveOffset({ portOffset, devInstance }), catch: (cause) => new DevRunnerError({ message: cause instanceof Error ? cause.message : String(cause), cause, }), }); const envOverrides = { noBrowser: readOptionalBooleanEnv("T3CODE_NO_BROWSER"), autoBootstrapProjectFromCwd: readOptionalBooleanEnv( "T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD", ), logWebSocketEvents: readOptionalBooleanEnv("T3CODE_LOG_WS_EVENTS"), }; const { serverOffset, webOffset } = yield* resolveModePortOffsets({ mode: input.mode, startOffset: offset, hasExplicitServerPort: input.port !== undefined, hasExplicitDevUrl: input.devUrl !== undefined, }); const env = yield* createDevRunnerEnv({ mode: input.mode, baseEnv: process.env, serverOffset, webOffset, stateDir: input.stateDir, authToken: input.authToken, noBrowser: resolveOptionalBooleanOverride(input.noBrowser, envOverrides.noBrowser), autoBootstrapProjectFromCwd: resolveOptionalBooleanOverride( input.autoBootstrapProjectFromCwd, envOverrides.autoBootstrapProjectFromCwd, ), logWebSocketEvents: resolveOptionalBooleanOverride( input.logWebSocketEvents, envOverrides.logWebSocketEvents, ), host: input.host, port: input.port, devUrl: input.devUrl, }); const selectionSuffix = serverOffset !== offset || webOffset !== offset ? ` selectedOffset(server=${serverOffset},web=${webOffset})` : ""; yield* Effect.logInfo( `[dev-runner] mode=${input.mode} source=${source}${selectionSuffix} serverPort=${String(env.T3CODE_PORT)} webPort=${String(env.PORT)} stateDir=${String(env.T3CODE_STATE_DIR)}`, ); if (input.dryRun) { return; } const child = yield* ChildProcess.make( "turbo", [...MODE_ARGS[input.mode], ...input.turboArgs], { stdin: "inherit", stdout: "inherit", stderr: "inherit", env, extendEnv: false, }, ); const exitCode = yield* child.exitCode; if (exitCode !== 0) { return yield* new DevRunnerError({ message: `turbo exited with code ${exitCode}`, }); } }).pipe( Effect.mapError((cause) => cause instanceof DevRunnerError ? cause : new DevRunnerError({ message: cause instanceof Error ? cause.message : "dev-runner failed", cause, }), ), ); } const devRunnerCli = Command.make("dev-runner", { mode: Argument.choice("mode", DEV_RUNNER_MODES).pipe( Argument.withDescription("Development mode to run."), ), stateDir: Flag.string("state-dir").pipe( Flag.withDescription("State directory path (forwards to T3CODE_STATE_DIR)."), Flag.withFallbackConfig(optionalStringConfig("T3CODE_STATE_DIR")), ), authToken: Flag.string("auth-token").pipe( Flag.withDescription("Auth token (forwards to T3CODE_AUTH_TOKEN)."), Flag.withAlias("token"), Flag.withFallbackConfig(optionalStringConfig("T3CODE_AUTH_TOKEN")), ), noBrowser: Flag.boolean("no-browser").pipe( Flag.withDescription("Browser auto-open toggle (equivalent to T3CODE_NO_BROWSER)."), Flag.withFallbackConfig(optionalBooleanConfig("T3CODE_NO_BROWSER")), ), autoBootstrapProjectFromCwd: Flag.boolean("auto-bootstrap-project-from-cwd").pipe( Flag.withDescription( "Auto-bootstrap toggle (equivalent to T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD).", ), Flag.withFallbackConfig(optionalBooleanConfig("T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD")),