diff --git a/.changeset/snapshot-dev-host-environment.md b/.changeset/snapshot-dev-host-environment.md new file mode 100644 index 000000000..3a19bdf0a --- /dev/null +++ b/.changeset/snapshot-dev-host-environment.md @@ -0,0 +1,5 @@ +--- +"agent-bundle": patch +--- + +Keep development host cleanup bound to the environment used when the host was installed. (#678) diff --git a/packages/agent-bundle/src/dev/host-install-manager.ts b/packages/agent-bundle/src/dev/host-install-manager.ts index 5c2731660..550dac1d3 100644 --- a/packages/agent-bundle/src/dev/host-install-manager.ts +++ b/packages/agent-bundle/src/dev/host-install-manager.ts @@ -21,8 +21,10 @@ import { platformRunOf } from './platform-run.ts'; import type { DevPlatformRuntime } from './platform-runtime.ts'; import type { Diagnostic } from '../core/diagnostics.ts'; import { + defaultCommandRunner, installBundle as defaultInstallBundle, type InstallBundleOptions, + type InstallCommandRunner, type InstallHost, type InstallResult, } from '../install/install.ts'; @@ -329,6 +331,7 @@ const syncDiagnostic = (host: InstallHost, epochId: string, error: unknown): Dia /** Owns opt-in host development installs for one foreground dev session. */ export class DevHostInstallManager { readonly #adoption: EpochAdoptionSource | undefined; + readonly #commandRunner: InstallCommandRunner; readonly #epochStore: EpochReferenceSource; readonly #environment: Readonly; readonly #eventHub: ProjectEventHub; @@ -345,10 +348,20 @@ export class DevHostInstallManager { constructor(options: DevHostInstallManagerOptions) { this.#adoption = options.adoption; + this.#environment = Object.freeze({ ...(options.environment ?? process.env) }); + this.#commandRunner = Object.freeze({ + run: ( + command: string, + args: readonly string[], + commandOptions: { readonly cwd: string; readonly environment?: Readonly }, + ) => defaultCommandRunner.run(command, args, { + ...commandOptions, + environment: this.#environment, + }), + }); this.#epochStore = options.epochStore; - this.#environment = options.environment ?? process.env; this.#eventHub = options.eventHub; - this.#home = options.home; + this.#home = options.home ?? homedir(); this.#hosts = Object.freeze([...new Set(options.hosts)]); this.#installBundle = options.installBundle ?? defaultInstallBundle; this.#projectRoot = resolve(options.projectRoot); @@ -439,6 +452,7 @@ export class DevHostInstallManager { const root = stableDevBundle(this.#projectRoot, host); try { await this.#uninstallBundle({ + commandRunner: this.#commandRunner, environment: this.#environment, force: true, from: root, @@ -463,6 +477,7 @@ export class DevHostInstallManager { if (installed === undefined) { if (host !== 'cursor') await ensureStableDevBundle(prepared.root, source); const result = await this.#installBundle({ + commandRunner: this.#commandRunner, environment: this.#environment, from: source, ...(this.#home === undefined ? {} : { home: this.#home }), diff --git a/packages/agent-bundle/src/install/install.ts b/packages/agent-bundle/src/install/install.ts index dbe6b9e2f..8936a67c5 100644 --- a/packages/agent-bundle/src/install/install.ts +++ b/packages/agent-bundle/src/install/install.ts @@ -71,7 +71,7 @@ export interface InstallCommandRunner { run( command: string, args: readonly string[], - options: { readonly cwd: string }, + options: { readonly cwd: string; readonly environment?: Readonly }, ): Promise; } @@ -125,9 +125,12 @@ export const defaultCommandRunner: InstallCommandRunner = Object.freeze({ run: ( command: string, args: readonly string[], - options: { readonly cwd: string }, + options: { readonly cwd: string; readonly environment?: Readonly }, ): Promise => new Promise((resolvePromise, reject) => { - execFile(command, [...args], { cwd: options.cwd }, (error, stdout, stderr) => { + execFile(command, [...args], { + cwd: options.cwd, + ...(options.environment === undefined ? {} : { env: options.environment }), + }, (error, stdout, stderr) => { if (error !== null && isErrno(error, 'ENOENT')) { reject(error); return;