950 lines · 35.6 KB · TypeScript
RawDownload
// @effect-diagnostics nodeBuiltinImport:off - Effect has no incremental digest or free-space query.import * as EffectNodeStream from "@effect/platform-node/NodeStream";import { ProviderDriverKind, type ProviderInstallState } from "@t3tools/contracts";import {  HostProcessArchitecture,  HostProcessEnvironment,  HostProcessPlatform,} from "@t3tools/shared/hostProcess";import * as Clock from "effect/Clock";import * as Cause from "effect/Cause";import * as Context from "effect/Context";import * as Crypto from "effect/Crypto";import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as Fiber from "effect/Fiber";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Path from "effect/Path";import * as Schema from "effect/Schema";import * as Scope from "effect/Scope";import * as Semaphore from "effect/Semaphore";import * as Stream from "effect/Stream";import * as SubscriptionRef from "effect/SubscriptionRef";import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";import * as NodeCrypto from "node:crypto";import * as NodeFSP from "node:fs/promises";import type * as NodeStream from "node:stream";import * as Yauzl from "yauzl"; import { ServerConfig } from "../config.ts";import { makeAntigravityAcpRuntime } from "./acp/AntigravityAcpSupport.ts";import {  buildAntigravityAcpSpawnInput,  prepareAntigravityProfile,} from "./antigravityAuthSupport.ts";import {  resolveAntigravityReleaseAsset,  type AntigravityReleaseAsset,} from "./antigravityRelease.ts"; const DRIVER = ProviderDriverKind.make("antigravity");const DOWNLOAD_TIMEOUT = "45 minutes";const VALIDATION_TIMEOUT = "90 seconds";const FREE_SPACE_MARGIN = 256 * 1024 * 1024;const RECORD_MAX_BYTES = 8 * 1024;const RELEASE_RECORD = ".install-complete.json"; const ReleaseId = Schema.String.check(Schema.isPattern(/^[a-f0-9]{64}$/u));const ActiveRelease = Schema.Struct({ releaseId: ReleaseId });const InstalledRelease = Schema.Struct({  releaseId: ReleaseId,  version: Schema.String,  executable: Schema.Struct({ name: Schema.String, bytes: Schema.Number }),  harness: Schema.Struct({ name: Schema.String, bytes: Schema.Number }),});type InstalledRelease = typeof InstalledRelease.Type;const encodeActiveRelease = Schema.encodeEffect(Schema.fromJsonString(ActiveRelease));const encodeInstalledRelease = Schema.encodeEffect(Schema.fromJsonString(InstalledRelease)); export class AntigravityInstallationError extends Schema.TaggedError<AntigravityInstallationError>()(  "AntigravityInstallationError",  {    operation: Schema.String,    detail: Schema.String,    cause: Schema.optional(Schema.Defect()),  },) {  override get message() {    return this.detail;  }}const isInstallationError = Schema.is(AntigravityInstallationError); export interface AntigravityExecutable {  readonly executablePath: string;  readonly harnessPath: string;  readonly source: "override" | "managed" | "path";  readonly version: string | null;  readonly managedVersionDirectory: string | null;} interface AntigravityInstallationService {  readonly managedDirectory: string;  readonly resolve: (    binaryPath?: string,    environment?: NodeJS.ProcessEnv,  ) => Effect.Effect<AntigravityExecutable, AntigravityInstallationError>;  /** Hold the lease until the spawned process has exited. */  readonly acquire: (    binaryPath?: string,    environment?: NodeJS.ProcessEnv,  ) => Effect.Effect<AntigravityExecutable, AntigravityInstallationError, Scope.Scope>;  readonly start: Effect.Effect<ProviderInstallState, AntigravityInstallationError>;  readonly cancel: (    operationId: string,  ) => Effect.Effect<ProviderInstallState, AntigravityInstallationError>;  readonly state: Effect.Effect<ProviderInstallState>;  readonly changes: Stream.Stream<ProviderInstallState>;  readonly remove: (    protectedBinaryPaths?: ReadonlyArray<string>,  ) => Effect.Effect<void, AntigravityInstallationError>;} export class AntigravityInstallation extends Context.Service<  AntigravityInstallation,  AntigravityInstallationService>()("t3/provider/AntigravityInstallation") {  static readonly layer = Layer.effect(    AntigravityInstallation,    Effect.gen(function* () {      const config = yield* ServerConfig;      return yield* makeAntigravityInstallation({ baseDir: config.baseDir });    }),  );} export interface AntigravityInstallationOptions {  readonly baseDir: string;  readonly releaseAsset?: AntigravityReleaseAsset | null;  readonly validate?: (    executable: AntigravityExecutable,    expectedVersion: string,  ) => Effect.Effect<void, AntigravityInstallationError, Scope.Scope>;} const installationError = (operation: string, detail: string, cause?: unknown) =>  new AntigravityInstallationError({    operation,    detail,    ...(cause === undefined ? {} : { cause }),  }); const wrapFailure = (operation: string, detail: string) => (cause: unknown) =>  isInstallationError(cause) ? cause : installationError(operation, detail, cause); function executableNames(platform: NodeJS.Platform) {  return platform === "win32"    ? { executable: "agy_acp_server.exe", harness: "localharness_external.exe" }    : { executable: "agy_acp_server.par", harness: "localharness_external" };} function isRunning(state: ProviderInstallState) {  return (    state.phase === "downloading" || state.phase === "extracting" || state.phase === "verifying"  );} /** Open only a verified local archive. Entries stay lazy and extraction stays bounded. */const openArchive = Effect.fn("AntigravityInstallation.openArchive")(function* (  archivePath: string,) {  const opened = yield* Effect.acquireRelease(    Effect.callback<      {        readonly zip: Yauzl.ZipFile;        readonly error: () => AntigravityInstallationError | undefined;        readonly close: Effect.Effect<void>;      },      AntigravityInstallationError    >((resume) => {      Yauzl.open(        archivePath,        { lazyEntries: true, autoClose: false, validateEntrySizes: true, strictFileNames: true },        (error, zip) => {          if (error || !zip) {            resume(              Effect.fail(                installationError("extract", "Could not open the verified archive.", error),              ),            );            return;          }          let closed = false;          let archiveError: AntigravityInstallationError | undefined;          zip.on("close", () => {            closed = true;          });          zip.on("error", (cause: unknown) => {            archiveError = installationError("extract", "The archive could not be read.", cause);          });          resume(            Effect.succeed({              zip,              error: () => archiveError,              close: Effect.callback<void>((finish) => {                if (closed) {                  finish(Effect.void);                  return;                }                const onClose = () => {                  zip.removeListener("error", onError);                  finish(Effect.void);                };                const onError = (cause: unknown) => {                  zip.removeListener("close", onClose);                  finish(                    Effect.die(installationError("extract", "Could not close the archive.", cause)),                  );                };                zip.once("close", onClose);                zip.once("error", onError);                zip.close();              }),            }),          );        },      );    }),    (opened) => opened.close,  );   const next = Effect.callback<Yauzl.Entry | null, AntigravityInstallationError>((resume) => {    const existingError = opened.error();    if (existingError) {      resume(Effect.fail(existingError));      return;    }    const cleanup = () => {      opened.zip.removeListener("entry", onEntry);      opened.zip.removeListener("end", onEnd);      opened.zip.removeListener("error", onError);    };    const onEntry = (entry: Yauzl.Entry) => {      cleanup();      resume(Effect.succeed(entry));    };    const onEnd = () => {      cleanup();      resume(Effect.succeed(null));    };    const onError = (cause: unknown) => {      cleanup();      resume(Effect.fail(installationError("extract", "The archive could not be read.", cause)));    };    opened.zip.once("entry", onEntry);    opened.zip.once("end", onEnd);    opened.zip.once("error", onError);    opened.zip.readEntry();    return Effect.sync(cleanup);  });   const streamEntry = (entry: Yauzl.Entry) =>    Effect.acquireRelease(      Effect.callback<NodeStream.Readable, AntigravityInstallationError>((resume) => {        opened.zip.openReadStream(entry, (cause, readable) => {          resume(            cause || !readable              ? Effect.fail(                  installationError("extract", "Could not read an archive member.", cause),                )              : Effect.succeed(readable),          );        });      }),      (readable) =>        Effect.sync(() => {          readable.destroy();        }),    );  return { entryCount: opened.zip.entryCount, next, streamEntry };}); export const makeAntigravityInstallation = Effect.fn("AntigravityInstallation.make")(function* (  options: AntigravityInstallationOptions,) {  const fs = yield* FileSystem.FileSystem;  const path = yield* Path.Path;  const crypto = yield* Crypto.Crypto;  const http = yield* HttpClient.HttpClient;  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;  const serviceScope = yield* Effect.scope;  const platform = yield* HostProcessPlatform;  const arch = yield* HostProcessArchitecture;  const environment = yield* HostProcessEnvironment;  const releaseAsset =    options.releaseAsset === undefined      ? resolveAntigravityReleaseAsset(platform, arch)      : options.releaseAsset;  const names = executableNames(platform);  const managedDirectory = path.join(    options.baseDir,    "tools",    "antigravity-acp",    `${platform}-${arch}`,  );  const versionsDirectory = path.join(managedDirectory, "versions");  const activePath = path.join(managedDirectory, "active.json");  const gate = yield* Semaphore.make(1);  const leases = new Map<string, number>();  let running: { readonly operationId: string; readonly fiber: Fiber.Fiber<void> } | undefined;  const state = yield* SubscriptionRef.make<ProviderInstallState>({    driver: DRIVER,    operationId: null,    phase: "idle",    downloadedBytes: 0,    totalBytes: releaseAsset?.archiveBytes ?? null,    version: releaseAsset?.version ?? null,    installedVersion: null,    canRemove: false,    message: null,  });   const readRecord = Effect.fn("AntigravityInstallation.readRecord")(function* <A>(    filePath: string,    schema: Schema.Codec<A>,  ) {    const info = yield* fs.stat(filePath);    if (info.type !== "File" || Number(info.size) > RECORD_MAX_BYTES) {      return yield* installationError(        "resolve",        "The managed runtime record is invalid. Reinstall Antigravity.",      );    }    const contents = yield* fs.readFileString(filePath);    return yield* Schema.decodeUnknownEffect(Schema.fromJsonString(schema))(contents);  });   const executableFile = Effect.fn("AntigravityInstallation.executableFile")(function* (    filePath: string,    bytes?: number,  ) {    const info = yield* fs.stat(filePath).pipe(Effect.option);    return (      Option.isSome(info) &&      info.value.type === "File" &&      (bytes === undefined || Number(info.value.size) === bytes) &&      (platform === "win32" || (info.value.mode & 0o111) !== 0)    );  });   const completedRelease = Effect.fn("AntigravityInstallation.completedRelease")(function* (    releaseId: string,  ) {    const directory = path.join(versionsDirectory, releaseId);    const record = yield* readRecord(path.join(directory, RELEASE_RECORD), InstalledRelease);    if (      record.releaseId !== releaseId ||      record.executable.name !== names.executable ||      record.harness.name !== names.harness ||      !Number.isSafeInteger(record.executable.bytes) ||      record.executable.bytes <= 0 ||      !Number.isSafeInteger(record.harness.bytes) ||      record.harness.bytes <= 0 ||      !record.version.trim() ||      !(yield* executableFile(path.join(directory, names.executable), record.executable.bytes)) ||      !(yield* executableFile(path.join(directory, names.harness), record.harness.bytes))    ) {      return yield* installationError(        "resolve",        "The managed Antigravity runtime is incomplete. Reinstall it.",      );    }    return {      executablePath: path.join(directory, names.executable),      harnessPath: path.join(directory, names.harness),      source: "managed",      version: record.version,      managedVersionDirectory: directory,    } satisfies AntigravityExecutable;  });   const fromExternal = Effect.fn("AntigravityInstallation.fromExternal")(function* (    candidate: string,    source: "override" | "path",  ) {    if (!(yield* executableFile(candidate))) return null;    const executablePath = yield* fs.realPath(candidate);    const directory = path.dirname(executablePath);    const harnessPath = path.join(directory, names.harness);    if (!(yield* executableFile(harnessPath))) return null;    const realVersions = yield* fs.realPath(versionsDirectory).pipe(Effect.option);    if (      Option.isSome(realVersions) &&      path.dirname(directory) === realVersions.value &&      /^[a-f0-9]{64}$/u.test(path.basename(directory))    ) {      const installed = yield* completedRelease(path.basename(directory));      return { ...installed, executablePath, harnessPath, source } satisfies AntigravityExecutable;    }    return {      executablePath,      harnessPath,      source,      version: null,      managedVersionDirectory: null,    } satisfies AntigravityExecutable;  });   const pathCandidates = (binary: string, processEnvironment = environment) => {    const pathValue =      platform === "win32"        ? Object.entries(processEnvironment).findLast(([key]) => key.toUpperCase() === "PATH")?.[1]        : processEnvironment.PATH;    return (pathValue ?? "")      .split(platform === "win32" ? ";" : ":")      .map((directory) => directory.trim().replace(/^"|"$/gu, ""))      .filter((directory) => directory.length > 0)      .map((directory) => path.resolve(directory, binary));  };   const resolve: AntigravityInstallationService["resolve"] = Effect.fn(    "AntigravityInstallation.resolve",  )(    function* (binaryPath?: string, processEnvironment?: NodeJS.ProcessEnv) {      const override = binaryPath?.trim();      if (override) {        const candidates =          path.isAbsolute(override) || override.includes("/") || override.includes("\\")            ? [path.resolve(override)]            : pathCandidates(override, processEnvironment);        for (const candidate of candidates) {          const selected = yield* fromExternal(candidate, "override");          if (selected) return selected;        }        return yield* installationError(          "resolve",          "The custom Antigravity executable or its localharness_external sibling is missing or not executable.",        );      }      if (yield* fs.exists(activePath)) {        const active = yield* readRecord(activePath, ActiveRelease);        return yield* completedRelease(active.releaseId);      }      for (const candidate of pathCandidates(names.executable, processEnvironment)) {        const selected = yield* fromExternal(candidate, "path");        if (selected) return selected;      }      return yield* installationError(        "resolve",        releaseAsset          ? "Antigravity is not installed. Install it in this environment or set a custom executable path."          : `Google does not publish an Antigravity runtime for ${platform}-${arch}. Use a supported environment or a custom executable.`,      );    },    Effect.mapError(      wrapFailure(        "resolve",        "Could not read the Antigravity installation. Reinstall it or set a custom executable path.",      ),    ),  );   const acquire = (binaryPath?: string, processEnvironment?: NodeJS.ProcessEnv) =>    Effect.acquireRelease(      gate.withPermit(        Effect.gen(function* () {          const executable = yield* resolve(binaryPath, processEnvironment);          const directory = executable.managedVersionDirectory;          if (directory) leases.set(directory, (leases.get(directory) ?? 0) + 1);          return executable;        }),      ),      (executable) =>        gate.withPermit(          Effect.sync(() => {            const directory = executable.managedVersionDirectory;            if (!directory) return;            const remaining = (leases.get(directory) ?? 1) - 1;            if (remaining > 0) leases.set(directory, remaining);            else leases.delete(directory);          }),        ),    );   const validate =    options.validate ??    Effect.fn("AntigravityInstallation.validate")(      function* (executable: AntigravityExecutable, expectedVersion: string) {        const profileDirectory = yield* fs.makeTempDirectoryScoped({          prefix: "t3-antigravity-validate-",        });        const profile = yield* prepareAntigravityProfile({          profileDirectory,          platform,          baseEnv: environment,        });        const runtime = yield* makeAntigravityAcpRuntime({          spawn: buildAntigravityAcpSpawnInput({            installation: executable,            profile,            cwd: profileDirectory,            baseEnv: environment,          }),          cwd: profileDirectory,          childProcessSpawner: spawner,          clientInfo: { name: "t3-code", version: "0.0.0" },        });        const initialized = yield* runtime.initialize();        if (          initialized.agentInfo?.name !== "antigravity-acp" ||          initialized.agentInfo.version !== expectedVersion ||          initialized.protocolVersion !== 1 ||          initialized.agentCapabilities?.loadSession !== true ||          !initialized.agentCapabilities.sessionCapabilities?.resume ||          !initialized.agentCapabilities.auth?.logout ||          !initialized.authMethods?.some((method) => method.id === "oauth-personal")        ) {          return yield* installationError(

Lines 1–500 of 950.