743 lines · 27.3 KB · TypeScript
RawDownload
import {  CommandId,  DEFAULT_MODEL,  DEFAULT_PROVIDER_INTERACTION_MODE,  type ModelSelection,  type Project,  ProjectId,  ProviderInstanceId,  ThreadId,} from "@t3tools/contracts";import * as Cause from "effect/Cause";import * as Console from "effect/Console";import * as Context from "effect/Context";import * as Crypto from "effect/Crypto";import * as DateTime from "effect/DateTime";import * as Deferred from "effect/Deferred";import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Path from "effect/Path";import * as Queue from "effect/Queue";import * as Ref from "effect/Ref";import * as Schema from "effect/Schema"; import * as ServerConfig from "./config.ts";import * as ServiceLauncherClient from "./cloud/serviceLauncherClient.ts";import * as Keybindings from "./keybindings.ts";import * as ExternalLauncher from "./process/externalLauncher.ts";import * as EffectWorker from "./orchestration-v2/EffectWorker.ts";import * as LegacyV1ThreadImporter from "./orchestration-v2/LegacyV1ThreadImporter.ts";import * as ProjectionMaintenance from "./orchestration-v2/ProjectionMaintenance.ts";import * as ProviderRuntimeRecovery from "./orchestration-v2/ProviderRuntimeRecoveryService.ts";import * as ProviderSessionManager from "./orchestration-v2/ProviderSessionManager.ts";import * as ThreadLaunch from "./orchestration-v2/ThreadLaunchService.ts";import * as ThreadManagement from "./orchestration-v2/ThreadManagementService.ts";import * as ProjectService from "./project/ProjectService.ts";import * as GitVcsDriver from "./vcs/GitVcsDriver.ts";import { ProjectionSnapshotQuery } from "./orchestration/Services/ProjectionSnapshotQuery.ts";import * as AgentAwarenessRelay from "./relay/AgentAwarenessRelay.ts";import * as ServerLifecycleEvents from "./serverLifecycleEvents.ts";import * as ServerSettings from "./serverSettings.ts";import { forkParked, forkParkedFiber } from "./serverActivation.ts";import * as AnalyticsService from "./telemetry/AnalyticsService.ts";import * as ServerEnvironment from "./environment/ServerEnvironment.ts";import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts";import {  formatHeadlessServeOutput,  formatHostForUrl,  isWildcardHost,  issueHeadlessServeAccessInfo,} from "./startupAccess.ts"; export class ServerRuntimeStartupError extends Schema.TaggedErrorClass<ServerRuntimeStartupError>()(  "ServerRuntimeStartupError",  {    mode: ServerConfig.RuntimeMode,    host: Schema.NullOr(Schema.String),    port: Schema.Number,    cause: Schema.Defect(),  },) {  override get message(): string {    return "Server runtime startup failed before command readiness.";  }} export class ServerRuntimeStartup extends Context.Service<  ServerRuntimeStartup,  {    readonly awaitCommandReady: Effect.Effect<void, ServerRuntimeStartupError>;    readonly markHttpListening: Effect.Effect<void>;    readonly enqueueCommand: <A, E>(      effect: Effect.Effect<A, E>,    ) => Effect.Effect<A, E | ServerRuntimeStartupError>;  }>()("t3/serverRuntimeStartup") {} interface QueuedCommand {  readonly run: Effect.Effect<void, never>;} type CommandReadinessState = "pending" | "ready" | ServerRuntimeStartupError; interface CommandGate {  readonly awaitCommandReady: Effect.Effect<void, ServerRuntimeStartupError>;  readonly signalCommandReady: Effect.Effect<void>;  readonly failCommandReady: (error: ServerRuntimeStartupError) => Effect.Effect<void>;  readonly enqueueCommand: <A, E>(    effect: Effect.Effect<A, E>,  ) => Effect.Effect<A, E | ServerRuntimeStartupError>;} const settleQueuedCommand = <A, E>(deferred: Deferred.Deferred<A, E>, exit: Exit.Exit<A, E>) =>  Exit.isSuccess(exit)    ? Deferred.succeed(deferred, exit.value)    : Deferred.failCause(deferred, exit.cause); export const makeCommandGate = Effect.gen(function* () {  const commandReady = yield* Deferred.make<void, ServerRuntimeStartupError>();  const commandQueue = yield* Queue.unbounded<QueuedCommand>();  const commandReadinessState = yield* Ref.make<CommandReadinessState>("pending");   const commandWorker = Effect.forever(    Queue.take(commandQueue).pipe(Effect.flatMap((command) => command.run)),  );  yield* Effect.forkScoped(commandWorker);   return {    awaitCommandReady: Deferred.await(commandReady),    signalCommandReady: Effect.gen(function* () {      yield* Ref.set(commandReadinessState, "ready");      yield* Deferred.succeed(commandReady, undefined).pipe(Effect.orDie);    }),    failCommandReady: (error) =>      Effect.gen(function* () {        yield* Ref.set(commandReadinessState, error);        yield* Deferred.fail(commandReady, error).pipe(Effect.orDie);      }),    enqueueCommand: <A, E>(effect: Effect.Effect<A, E>) =>      Effect.gen(function* () {        const readinessState = yield* Ref.get(commandReadinessState);        if (readinessState === "ready") {          return yield* effect;        }        if (readinessState !== "pending") {          return yield* readinessState;        }         const result = yield* Deferred.make<A, E | ServerRuntimeStartupError>();        yield* Queue.offer(commandQueue, {          run: Deferred.await(commandReady).pipe(            Effect.flatMap(() => effect),            Effect.exit,            Effect.flatMap((exit) => settleQueuedCommand(result, exit)),          ),        });        return yield* Deferred.await(result);      }),  } satisfies CommandGate;}); export const recordStartupHeartbeat = Effect.gen(function* () {  const analytics = yield* AnalyticsService.AnalyticsService;  const projects = yield* ProjectService.ProjectService;  const threads = yield* ThreadManagement.ThreadManagementService;   const { threadCount, projectCount } = yield* Effect.all({    projects: projects.snapshot,    threads: threads.getShellSnapshot(),  }).pipe(    Effect.map(({ projects: projectSnapshot, threads: shellSnapshot }) => ({      projectCount: projectSnapshot.projects.length,      threadCount: shellSnapshot.threads.length + shellSnapshot.archivedThreads.length,    })),    Effect.catch((cause) =>      Effect.logWarning("failed to gather V2 startup counts for telemetry", {        cause,      }).pipe(        Effect.as({          threadCount: 0,          projectCount: 0,        }),      ),    ),  );   yield* analytics.record("server.boot.heartbeat", {    threadCount,    projectCount,  });}); export const launchStartupHeartbeat = recordStartupHeartbeat.pipe(  Effect.annotateSpans({ "startup.phase": "heartbeat.record" }),  Effect.withSpan("server.startup.heartbeat.record"),  Effect.ignoreCause({ log: true }),  Effect.forkScoped,  Effect.asVoid,); export const getAutoBootstrapThreadModelSelection = (): ModelSelection => ({  instanceId: ProviderInstanceId.make("codex"),  model: DEFAULT_MODEL,}); interface AutoBootstrapWelcomeTargets {  readonly bootstrapProjectId?: ProjectId;  readonly bootstrapThreadId?: ThreadId;} export const autoPullProjects = Effect.fn("autoPullProjects")(function* (  projects: ReadonlyArray<Pick<Project, "workspaceRoot" | "autoPull">>,) {  const git = yield* GitVcsDriver.GitVcsDriver;  const workspaceRoots = [    ...new Set(      projects        .filter((project) => project.autoPull === true)        .map((project) => project.workspaceRoot),    ),  ];   yield* Effect.forEach(    workspaceRoots,    (cwd) =>      Effect.gen(function* () {        const status = yield* git.statusDetails(cwd);        if (          !status.isRepo ||          !status.isDefaultBranch ||          !status.hasUpstream ||          status.hasWorkingTreeChanges ||          status.aheadCount > 0        ) {          yield* Effect.logDebug("Skipped automatic project pull", {            cwd,            reason: !status.isRepo              ? "not-a-repository"              : !status.isDefaultBranch                ? "not-on-default-branch"                : !status.hasUpstream                  ? "no-upstream"                  : status.hasWorkingTreeChanges                    ? "working-tree-changes"                    : "local-commits",          });          return;        }         if (status.behindCount <= 0) return;         const result = yield* git.pullCurrentBranch(cwd);        yield* Effect.logDebug("Automatic project pull completed", {          cwd,          status: result.status,          refName: result.refName,        });      }).pipe(        Effect.catch((cause) =>          Effect.logWarning("Automatic project pull failed", {            cwd,            cause,          }),        ),      ),    { concurrency: 4, discard: true },  );}); export const resolveWelcomeBase = Effect.gen(function* () {  const serverConfig = yield* ServerConfig.ServerConfig;  const segments = serverConfig.cwd.split(/[/\\]/).filter(Boolean);  const projectName = segments[segments.length - 1] ?? "project";   return {    cwd: serverConfig.cwd,    projectName,  } as const;}); export const resolveAutoBootstrapWelcomeTargets = Effect.gen(function* () {  const crypto = yield* Crypto.Crypto;  const randomUUID = crypto.randomUUIDv4;  const serverConfig = yield* ServerConfig.ServerConfig;  const projects = yield* ProjectService.ProjectService;  const threads = yield* ThreadManagement.ThreadManagementService;  const threadLaunch = yield* ThreadLaunch.ThreadLaunchService;  const path = yield* Path.Path;   let bootstrapProjectId: ProjectId | undefined;  let bootstrapThreadId: ThreadId | undefined;   if (serverConfig.autoBootstrapProjectFromCwd) {    // Project creation has no user model choice; only the bootstrap thread    // gets an automatic selection, and an explicit project default wins.    const threadModelSelection = getAutoBootstrapThreadModelSelection();    const { project } = yield* projects.bootstrap({      commandId: CommandId.make(yield* randomUUID),      projectId: ProjectId.make(yield* randomUUID),      title: path.basename(serverConfig.cwd) || "project",      workspaceRoot: serverConfig.cwd,    });    const shell = yield* threads.getShellSnapshot();    const existingThread = shell.threads.find(      (thread) =>        thread.projectId === project.id && thread.lineage.relationshipToParent !== "subagent",    );    if (existingThread === undefined) {      const launched = yield* threadLaunch.launch({        commandId: CommandId.make(yield* randomUUID),        projectId: project.id,        title: "New thread",        modelSelection: project.defaultModelSelection ?? threadModelSelection,        interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,        runtimeMode: "full-access",        workspaceStrategy: { type: "root" },        createdBy: "system",        creationSource: "server",      });      bootstrapProjectId = project.id;      bootstrapThreadId = launched.threadId;    } else {      bootstrapProjectId = project.id;      bootstrapThreadId = existingThread.id;    }  }   return {    ...(bootstrapProjectId ? { bootstrapProjectId } : {}),    ...(bootstrapThreadId ? { bootstrapThreadId } : {}),  } satisfies AutoBootstrapWelcomeTargets;}); const resolveStartupBrowserTarget = Effect.gen(function* () {  const serverConfig = yield* ServerConfig.ServerConfig;  const serverAuth = yield* EnvironmentAuth.EnvironmentAuth;  const localUrl = `http://localhost:${serverConfig.port}`;  const bindUrl =    serverConfig.host && !isWildcardHost(serverConfig.host)      ? `http://${formatHostForUrl(serverConfig.host)}:${serverConfig.port}`      : localUrl;  const baseTarget = serverConfig.devUrl?.toString() ?? bindUrl;  return yield* Effect.succeed(serverConfig.mode === "desktop" ? baseTarget : undefined).pipe(    Effect.flatMap((target) =>      target ? Effect.succeed(target) : serverAuth.issueStartupPairingUrl(baseTarget),    ),  );}); const maybeOpenBrowser = (target: string) =>  Effect.gen(function* () {    const serverConfig = yield* ServerConfig.ServerConfig;    if (serverConfig.noBrowser) {      return;    }    const externalLauncher = yield* ExternalLauncher.ExternalLauncher;     yield* externalLauncher.launchBrowser(target).pipe(      Effect.catch(() =>        Effect.logInfo("browser auto-open unavailable", {          hint: `Open ${target} in your browser.`,        }),      ),    );  }); const runStartupPhase = <A, E, R>(phase: string, effect: Effect.Effect<A, E, R>) =>  effect.pipe(    Effect.annotateSpans({ "startup.phase": phase }),    Effect.withSpan(`server.startup.${phase}`),  ); interface StartupOptions {  readonly activate?: Effect.Effect<void>;  readonly awaitAuxiliaryParked?: Effect.Effect<void>;  readonly abort?: (error: ServerRuntimeStartupError) => Effect.Effect<void>;} export const startEffectWorkerWithRelay = Effect.fn(  "ServerRuntimeStartup.startEffectWorkerWithRelay",)(function* <WorkerContext, RelayContext>(input: {  readonly runWorker: Effect.Effect<void, never, WorkerContext>;  readonly startRelay: Effect.Effect<void, never, RelayContext>;  readonly workerFiberRef: Ref.Ref<Fiber.Fiber<void, never> | null>;}) {  const workerFiber = yield* forkParkedFiber(input.runWorker);  yield* Ref.set(input.workerFiberRef, workerFiber);  yield* input.startRelay.pipe(    Effect.onExit((exit) => {      if (Exit.isSuccess(exit)) {        return Effect.void;      }      return Ref.getAndSet(input.workerFiberRef, null).pipe(        Effect.flatMap((ownedWorkerFiber) =>          ownedWorkerFiber === null            ? Effect.void            : Fiber.interrupt(ownedWorkerFiber).pipe(Effect.asVoid),        ),      );    }),  );}); export function runOrderedV2StartupPhases<  Import,  Verification extends { readonly valid: boolean },  RebuildVerification extends { readonly valid: boolean },  Recovery,  Bootstrap,  ImportError,  VerifyError,  RebuildError,  RecoveryError,  WorkerError,  BootstrapError,  ImportContext,  VerifyContext,  RebuildContext,  RecoveryContext,  WorkerContext,  BootstrapContext,>(input: {  readonly importLegacyShells: Effect.Effect<Import, ImportError, ImportContext>;  readonly verify: Effect.Effect<Verification, VerifyError, VerifyContext>;  readonly rebuild: Effect.Effect<RebuildVerification, RebuildError, RebuildContext>;  readonly recover: Effect.Effect<Recovery, RecoveryError, RecoveryContext>;  readonly startEffectWorker: Effect.Effect<void, WorkerError, WorkerContext>;  readonly autoBootstrap: Effect.Effect<Bootstrap, BootstrapError, BootstrapContext>;}) {  return Effect.gen(function* () {    yield* input.importLegacyShells;    const verification = yield* input.verify;    if (!verification.valid) {      const rebuilt = yield* input.rebuild;      if (!rebuilt.valid) {        return yield* Effect.die(          new Error("V2 orchestration projection rebuild did not produce a valid projection."),        );      }    }    const recovery = yield* input.recover;    yield* input.startEffectWorker;    const bootstrap = yield* input.autoBootstrap;    return { recovery, bootstrap } as const;  });} export const make = (options?: StartupOptions) =>  Effect.gen(function* () {    const serverConfig = yield* ServerConfig.ServerConfig;    const keybindings = yield* Keybindings.Keybindings;    const projectionMaintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2;    const legacyV1ThreadImporter = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter;    const providerRuntimeRecovery = yield* ProviderRuntimeRecovery.ProviderRuntimeRecoveryService;    const providerSessions = yield* ProviderSessionManager.ProviderSessionManagerV2;    const agentAwarenessRelay = yield* AgentAwarenessRelay.AgentAwarenessRelay;    const lifecycleEvents = yield* ServerLifecycleEvents.ServerLifecycleEvents;    const serverSettings = yield* ServerSettings.ServerSettingsService;    const serverEnvironment = yield* ServerEnvironment.ServerEnvironment;    const crypto = yield* Crypto.Crypto;    const launcher = yield* ServiceLauncherClient.ServiceLauncherClient;     const commandGate = yield* makeCommandGate;    const httpListening = yield* Deferred.make<void>();    const effectWorkerFiber = yield* Ref.make<Fiber.Fiber<void, never> | null>(null);     yield* Effect.addFinalizer(() =>      Effect.gen(function* () {        yield* commandGate.failCommandReady(          new ServerRuntimeStartupError({            mode: serverConfig.mode,            host: serverConfig.host ?? null,            port: serverConfig.port,            cause: "Server runtime is shutting down.",          }),        );        const workerFiber = yield* Ref.getAndSet(effectWorkerFiber, null);        if (workerFiber !== null) {          yield* Fiber.interrupt(workerFiber).pipe(Effect.ignore);        }        yield* providerRuntimeRecovery.prepareForShutdown.pipe(          Effect.ensuring(providerSessions.shutdown),        );        const reconciliation = yield* providerRuntimeRecovery.reconcile("shutdown");        yield* Effect.logInfo("V2 orchestration shutdown reconciliation completed", reconciliation);      }).pipe(        Effect.catchCause((cause) =>          Effect.logWarning("V2 orchestration shutdown reconciliation failed", {            cause: Cause.pretty(cause),          }),        ),      ),    );     const startup = Effect.gen(function* () {      yield* Effect.logDebug("startup phase: starting keybindings runtime");      yield* runStartupPhase(        "keybindings.start",        keybindings.start.pipe(          Effect.catch((error) =>            Effect.logWarning("failed to start keybindings runtime", {              path: error.configPath,              detail: error.detail,              cause: error.cause,            }),          ),        ),      );       yield* Effect.logDebug("startup phase: starting server settings runtime");      yield* runStartupPhase(        "settings.start",        serverSettings.start.pipe(          Effect.catch((error) =>            Effect.logWarning("failed to start server settings runtime", {              path: error.settingsPath,              operation: error.operation,              providerInstanceId: error.providerInstanceId,              environmentVariable: error.environmentVariable,

Lines 1–500 of 743.