1180 lines · 39.7 KB · TypeScript
RawDownload
import { NetService } from "@t3tools/shared/Net";import { parsePersistedServerObservabilitySettings } from "@t3tools/shared/serverSettings";import {  AuthSessionId,  CommandId,  OrchestrationReadModel,  ProjectId,  type ClientOrchestrationCommand,} from "@t3tools/contracts";import {  Config,  Console,  Duration,  Effect,  Exit,  FileSystem,  Layer,  LogLevel,  Option,  Path,  References,  Schema,  SchemaIssue,  SchemaTransformation,} from "effect";import { Argument, Command, Flag, GlobalFlag } from "effect/unstable/cli";import {  FetchHttpClient,  HttpClient,  HttpClientRequest,  HttpClientResponse,} from "effect/unstable/http"; import {  DEFAULT_PORT,  deriveServerPaths,  ensureServerDirectories,  resolveStaticDir,  ServerConfig,  RuntimeMode,  type ServerConfigShape,  type StartupPresentation,} from "./config.ts";import { readBootstrapEnvelope } from "./bootstrap.ts";import { expandHomePath, resolveBaseDir } from "./os-jank.ts";import { runServer } from "./server.ts";import { AuthControlPlaneRuntimeLive } from "./auth/Layers/AuthControlPlane.ts";import {  formatIssuedPairingCredential,  formatIssuedSession,  formatPairingCredentialList,  formatSessionList,} from "./cliAuthFormat.ts";import { AuthControlPlane } from "./auth/Services/AuthControlPlane.ts";import type { AuthControlPlaneShape } from "./auth/Services/AuthControlPlane.ts";import { OrchestrationEngineService } from "./orchestration/Services/OrchestrationEngine.ts";import { ProjectionSnapshotQuery } from "./orchestration/Services/ProjectionSnapshotQuery.ts";import { OrchestrationLayerLive } from "./orchestration/runtimeLayer.ts";import { layerConfig as SqlitePersistenceLayerLive } from "./persistence/Layers/Sqlite.ts";import { RepositoryIdentityResolverLive } from "./project/Layers/RepositoryIdentityResolver.ts";import { getAutoBootstrapDefaultModelSelection } from "./serverRuntimeStartup.ts";import {  clearPersistedServerRuntimeState,  readPersistedServerRuntimeState,} from "./serverRuntimeState.ts";import { WorkspacePaths } from "./workspace/Services/WorkspacePaths.ts";import { WorkspacePathsLive } from "./workspace/Layers/WorkspacePaths.ts"; const PortSchema = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 65535 })); const BootstrapEnvelopeSchema = Schema.Struct({  mode: Schema.optional(RuntimeMode),  port: Schema.optional(PortSchema),  host: Schema.optional(Schema.String),  t3Home: Schema.optional(Schema.String),  devUrl: Schema.optional(Schema.URLFromString),  noBrowser: Schema.optional(Schema.Boolean),  desktopBootstrapToken: Schema.optional(Schema.String),  autoBootstrapProjectFromCwd: Schema.optional(Schema.Boolean),  logWebSocketEvents: Schema.optional(Schema.Boolean),  tailscaleServeEnabled: Schema.optional(Schema.Boolean),  tailscaleServePort: Schema.optional(PortSchema),  otlpTracesUrl: Schema.optional(Schema.String),  otlpMetricsUrl: Schema.optional(Schema.String),}); const modeFlag = Flag.choice("mode", RuntimeMode.literals).pipe(  Flag.withDescription("Runtime mode. `desktop` keeps loopback defaults unless overridden."),  Flag.optional,);const portFlag = Flag.integer("port").pipe(  Flag.withSchema(PortSchema),  Flag.withDescription("Port for the HTTP/WebSocket server."),  Flag.optional,);const hostFlag = Flag.string("host").pipe(  Flag.withDescription("Host/interface to bind (for example 127.0.0.1, 0.0.0.0, or a Tailnet IP)."),  Flag.optional,);const baseDirFlag = Flag.string("base-dir").pipe(  Flag.withDescription("Base directory path (equivalent to T3CODE_HOME)."),  Flag.optional,);const devUrlFlag = Flag.string("dev-url").pipe(  Flag.withSchema(Schema.URLFromString),  Flag.withDescription("Dev web URL to proxy/redirect to (equivalent to VITE_DEV_SERVER_URL)."),  Flag.optional,);const noBrowserFlag = Flag.boolean("no-browser").pipe(  Flag.withDescription("Disable automatic browser opening."),  Flag.optional,);const bootstrapFdFlag = Flag.integer("bootstrap-fd").pipe(  Flag.withSchema(Schema.Int),  Flag.withDescription("Read one-time bootstrap secrets from the given file descriptor."),  Flag.optional,);const autoBootstrapProjectFromCwdFlag = Flag.boolean("auto-bootstrap-project-from-cwd").pipe(  Flag.withDescription(    "Create a project for the current working directory on startup when missing.",  ),  Flag.optional,);const logWebSocketEventsFlag = Flag.boolean("log-websocket-events").pipe(  Flag.withDescription(    "Emit server-side logs for outbound WebSocket push traffic (equivalent to T3CODE_LOG_WS_EVENTS).",  ),  Flag.withAlias("log-ws-events"),  Flag.optional,);const tailscaleServeFlag = Flag.boolean("tailscale-serve").pipe(  Flag.withDescription(    "Configure Tailscale Serve to expose this backend over HTTPS on the Tailnet.",  ),  Flag.optional,);const tailscaleServePortFlag = Flag.integer("tailscale-serve-port").pipe(  Flag.withSchema(PortSchema),  Flag.withDescription("HTTPS port for Tailscale Serve when --tailscale-serve is enabled."),  Flag.optional,); const EnvServerConfig = Config.all({  logLevel: Config.logLevel("T3CODE_LOG_LEVEL").pipe(Config.withDefault("Info")),  traceMinLevel: Config.logLevel("T3CODE_TRACE_MIN_LEVEL").pipe(Config.withDefault("Info")),  traceTimingEnabled: Config.boolean("T3CODE_TRACE_TIMING_ENABLED").pipe(Config.withDefault(true)),  traceFile: Config.string("T3CODE_TRACE_FILE").pipe(    Config.option,    Config.map(Option.getOrUndefined),  ),  traceMaxBytes: Config.int("T3CODE_TRACE_MAX_BYTES").pipe(Config.withDefault(10 * 1024 * 1024)),  traceMaxFiles: Config.int("T3CODE_TRACE_MAX_FILES").pipe(Config.withDefault(10)),  traceBatchWindowMs: Config.int("T3CODE_TRACE_BATCH_WINDOW_MS").pipe(Config.withDefault(200)),  otlpTracesUrl: Config.string("T3CODE_OTLP_TRACES_URL").pipe(    Config.option,    Config.map(Option.getOrUndefined),  ),  otlpMetricsUrl: Config.string("T3CODE_OTLP_METRICS_URL").pipe(    Config.option,    Config.map(Option.getOrUndefined),  ),  otlpExportIntervalMs: Config.int("T3CODE_OTLP_EXPORT_INTERVAL_MS").pipe(    Config.withDefault(10_000),  ),  otlpServiceName: Config.string("T3CODE_OTLP_SERVICE_NAME").pipe(Config.withDefault("t3-server")),  mode: Config.schema(RuntimeMode, "T3CODE_MODE").pipe(    Config.option,    Config.map(Option.getOrUndefined),  ),  port: Config.port("T3CODE_PORT").pipe(Config.option, Config.map(Option.getOrUndefined)),  host: Config.string("T3CODE_HOST").pipe(Config.option, Config.map(Option.getOrUndefined)),  t3Home: Config.string("T3CODE_HOME").pipe(Config.option, Config.map(Option.getOrUndefined)),  devUrl: Config.url("VITE_DEV_SERVER_URL").pipe(Config.option, Config.map(Option.getOrUndefined)),  noBrowser: Config.boolean("T3CODE_NO_BROWSER").pipe(    Config.option,    Config.map(Option.getOrUndefined),  ),  bootstrapFd: Config.int("T3CODE_BOOTSTRAP_FD").pipe(    Config.option,    Config.map(Option.getOrUndefined),  ),  autoBootstrapProjectFromCwd: Config.boolean("T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD").pipe(    Config.option,    Config.map(Option.getOrUndefined),  ),  logWebSocketEvents: Config.boolean("T3CODE_LOG_WS_EVENTS").pipe(    Config.option,    Config.map(Option.getOrUndefined),  ),  tailscaleServeEnabled: Config.boolean("T3CODE_TAILSCALE_SERVE").pipe(    Config.option,    Config.map(Option.getOrUndefined),  ),  tailscaleServePort: Config.port("T3CODE_TAILSCALE_SERVE_PORT").pipe(    Config.option,    Config.map(Option.getOrUndefined),  ),}); interface CliServerFlags {  readonly mode: Option.Option<RuntimeMode>;  readonly port: Option.Option<number>;  readonly host: Option.Option<string>;  readonly baseDir: Option.Option<string>;  readonly cwd: Option.Option<string>;  readonly devUrl: Option.Option<URL>;  readonly noBrowser: Option.Option<boolean>;  readonly bootstrapFd: Option.Option<number>;  readonly autoBootstrapProjectFromCwd: Option.Option<boolean>;  readonly logWebSocketEvents: Option.Option<boolean>;  readonly tailscaleServeEnabled: Option.Option<boolean>;  readonly tailscaleServePort: Option.Option<number>;} interface CliAuthLocationFlags {  readonly baseDir: Option.Option<string>;  readonly devUrl?: Option.Option<URL>;} const resolveOptionPrecedence = <Value>(  ...values: ReadonlyArray<Option.Option<Value>>): Option.Option<Value> => Option.firstSomeOf(values); const loadPersistedObservabilitySettings = Effect.fn(function* (settingsPath: string) {  const fs = yield* FileSystem.FileSystem;  const exists = yield* fs.exists(settingsPath).pipe(Effect.orElseSucceed(() => false));  if (!exists) {    return { otlpTracesUrl: undefined, otlpMetricsUrl: undefined };  }   const raw = yield* fs.readFileString(settingsPath).pipe(Effect.orElseSucceed(() => ""));  return parsePersistedServerObservabilitySettings(raw);}); export const resolveServerConfig = (  flags: CliServerFlags,  cliLogLevel: Option.Option<LogLevel.LogLevel>,  options?: {    readonly startupPresentation?: StartupPresentation;    readonly forceAutoBootstrapProjectFromCwd?: boolean;  },) =>  Effect.gen(function* () {    const { findAvailablePort } = yield* NetService;    const path = yield* Path.Path;    const fs = yield* FileSystem.FileSystem;    const env = yield* EnvServerConfig;    const normalizedFlags = {      mode: flags.mode ?? Option.none(),      port: flags.port ?? Option.none(),      host: flags.host ?? Option.none(),      baseDir: flags.baseDir ?? Option.none(),      cwd: flags.cwd ?? Option.none(),      devUrl: flags.devUrl ?? Option.none(),      noBrowser: flags.noBrowser ?? Option.none(),      bootstrapFd: flags.bootstrapFd ?? Option.none(),      autoBootstrapProjectFromCwd: flags.autoBootstrapProjectFromCwd ?? Option.none(),      logWebSocketEvents: flags.logWebSocketEvents ?? Option.none(),      tailscaleServeEnabled: flags.tailscaleServeEnabled ?? Option.none(),      tailscaleServePort: flags.tailscaleServePort ?? Option.none(),    } satisfies CliServerFlags;    const bootstrapFd = Option.getOrUndefined(normalizedFlags.bootstrapFd) ?? env.bootstrapFd;    const bootstrapEnvelope =      bootstrapFd !== undefined        ? yield* readBootstrapEnvelope(BootstrapEnvelopeSchema, bootstrapFd)        : Option.none();    const bootstrap = Option.getOrUndefined(bootstrapEnvelope);     const mode: RuntimeMode = Option.getOrElse(      resolveOptionPrecedence(        normalizedFlags.mode,        Option.fromUndefinedOr(env.mode),        Option.fromUndefinedOr(bootstrap?.mode),      ),      () => "web",    );     const port = yield* Option.match(      resolveOptionPrecedence(        normalizedFlags.port,        Option.fromUndefinedOr(env.port),        Option.fromUndefinedOr(bootstrap?.port),      ),      {        onSome: (value) => Effect.succeed(value),        onNone: () => {          if (mode === "desktop") {            return Effect.succeed(DEFAULT_PORT);          }          return findAvailablePort(DEFAULT_PORT);        },      },    );    const devUrl = Option.getOrElse(      resolveOptionPrecedence(        normalizedFlags.devUrl,        Option.fromUndefinedOr(env.devUrl),        Option.fromUndefinedOr(bootstrap?.devUrl),      ),      () => undefined,    );    const baseDir = yield* resolveBaseDir(      Option.getOrUndefined(        resolveOptionPrecedence(          normalizedFlags.baseDir,          Option.fromUndefinedOr(env.t3Home),          Option.fromUndefinedOr(bootstrap?.t3Home),        ),      ),    );    const rawCwd = Option.getOrElse(normalizedFlags.cwd, () => process.cwd());    const cwd = path.resolve(yield* expandHomePath(rawCwd.trim()));    yield* fs.makeDirectory(cwd, { recursive: true });    const derivedPaths = yield* deriveServerPaths(baseDir, devUrl);    yield* ensureServerDirectories(derivedPaths);    const persistedObservabilitySettings = yield* loadPersistedObservabilitySettings(      derivedPaths.settingsPath,    );    const serverTracePath = env.traceFile ?? derivedPaths.serverTracePath;    yield* fs.makeDirectory(path.dirname(serverTracePath), { recursive: true });    const startupPresentation = options?.startupPresentation ?? "browser";    const isHeadlessStartup = startupPresentation === "headless";    const noBrowser = Option.getOrElse(      resolveOptionPrecedence(        isHeadlessStartup ? Option.some(true) : Option.none(),        normalizedFlags.noBrowser,        Option.fromUndefinedOr(env.noBrowser),        Option.fromUndefinedOr(bootstrap?.noBrowser),      ),      () => mode === "desktop",    );    const desktopBootstrapToken = bootstrap?.desktopBootstrapToken;    const autoBootstrapProjectFromCwd = Option.getOrElse(      resolveOptionPrecedence(        Option.fromUndefinedOr(options?.forceAutoBootstrapProjectFromCwd),        isHeadlessStartup ? Option.some(false) : Option.none(),        normalizedFlags.autoBootstrapProjectFromCwd,        Option.fromUndefinedOr(env.autoBootstrapProjectFromCwd),        Option.fromUndefinedOr(bootstrap?.autoBootstrapProjectFromCwd),      ),      () => mode === "web",    );    const logWebSocketEvents = Option.getOrElse(      resolveOptionPrecedence(        normalizedFlags.logWebSocketEvents,        Option.fromUndefinedOr(env.logWebSocketEvents),        Option.fromUndefinedOr(bootstrap?.logWebSocketEvents),      ),      () => Boolean(devUrl),    );    const tailscaleServeEnabled = Option.getOrElse(      resolveOptionPrecedence(        normalizedFlags.tailscaleServeEnabled,        Option.fromUndefinedOr(env.tailscaleServeEnabled),        Option.fromUndefinedOr(bootstrap?.tailscaleServeEnabled),      ),      () => false,    );    const tailscaleServePort = Option.getOrElse(      resolveOptionPrecedence(        normalizedFlags.tailscaleServePort,        Option.fromUndefinedOr(env.tailscaleServePort),        Option.fromUndefinedOr(bootstrap?.tailscaleServePort),      ),      () => 443,    );    const staticDir = devUrl ? undefined : yield* resolveStaticDir();    const host = Option.getOrElse(      resolveOptionPrecedence(        normalizedFlags.host,        Option.fromUndefinedOr(env.host),        Option.fromUndefinedOr(bootstrap?.host),      ),      () => (mode === "desktop" ? "127.0.0.1" : undefined),    );    const logLevel = Option.getOrElse(cliLogLevel, () => env.logLevel);     const config: ServerConfigShape = {      logLevel,      traceMinLevel: env.traceMinLevel,      traceTimingEnabled: env.traceTimingEnabled,      traceBatchWindowMs: env.traceBatchWindowMs,      traceMaxBytes: env.traceMaxBytes,      traceMaxFiles: env.traceMaxFiles,      otlpTracesUrl:        env.otlpTracesUrl ??        bootstrap?.otlpTracesUrl ??        persistedObservabilitySettings.otlpTracesUrl,      otlpMetricsUrl:        env.otlpMetricsUrl ??        bootstrap?.otlpMetricsUrl ??        persistedObservabilitySettings.otlpMetricsUrl,      otlpExportIntervalMs: env.otlpExportIntervalMs,      otlpServiceName: env.otlpServiceName,      mode,      port,      cwd,      baseDir,      ...derivedPaths,      serverTracePath,      host,      staticDir,      devUrl,      noBrowser,      startupPresentation,      desktopBootstrapToken,      autoBootstrapProjectFromCwd,      logWebSocketEvents,      tailscaleServeEnabled,      tailscaleServePort,    };     return config;  }); const resolveCliAuthConfig = (  flags: CliAuthLocationFlags,  cliLogLevel: Option.Option<LogLevel.LogLevel>,) =>  resolveServerConfig(    {      mode: Option.none(),      port: Option.none(),      host: Option.none(),      baseDir: flags.baseDir,      cwd: Option.none(),      devUrl: flags.devUrl ?? Option.none(),      noBrowser: Option.none(),      bootstrapFd: Option.none(),      autoBootstrapProjectFromCwd: Option.none(),      logWebSocketEvents: Option.none(),      tailscaleServeEnabled: Option.none(),      tailscaleServePort: Option.none(),    },    cliLogLevel,  ); const DurationShorthandPattern = /^(?<value>\d+)(?<unit>ms|s|m|h|d|w)$/i; const parseDurationInput = (value: string): Duration.Duration | null => {  const trimmed = value.trim();  if (trimmed.length === 0) return null;   const shorthand = DurationShorthandPattern.exec(trimmed);  const normalizedInput = shorthand?.groups    ? (() => {        const amountText = shorthand.groups.value;        const unitText = shorthand.groups.unit;        if (typeof amountText !== "string" || typeof unitText !== "string") {          return null;        }         const amount = Number.parseInt(amountText, 10);        if (!Number.isFinite(amount)) return null;         switch (unitText.toLowerCase()) {          case "ms":            return `${amount} millis`;          case "s":            return `${amount} seconds`;          case "m":            return `${amount} minutes`;          case "h":            return `${amount} hours`;          case "d":            return `${amount} days`;          case "w":            return `${amount} weeks`;          default:            return null;        }      })()    : (trimmed as Duration.Input);   if (normalizedInput === null) return null;   const decoded = Duration.fromInput(normalizedInput as Duration.Input);  return Option.isSome(decoded) ? decoded.value : null;}; const DurationFromString = Schema.String.pipe(  Schema.decodeTo(    Schema.Duration,    SchemaTransformation.transformOrFail({      decode: (value) => {        const duration = parseDurationInput(value);        if (duration !== null) {          return Effect.succeed(duration);        }        return Effect.fail(          new SchemaIssue.InvalidValue(Option.some(value), {            message: "Invalid duration. Use values like 5m, 1h, 30d, or 15 minutes.",          }),        );      },      encode: (duration) => Effect.succeed(Duration.format(duration)),    }),  ),); 

Lines 1–500 of 1180.