File size: 2,289 Bytes
d197cf3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import { isDeepStrictEqual } from "node:util";
import type { ClawRemoveApplyOptions } from "./lifecycle-remove-contract.js";
import type { ClawPackageRemovalPhaseResult } from "./package-remove-contract.js";
import {
  filterReferencedCleanup,
  digestClawPackageRemovalPlan,
  digestClawRemovalInstall,
  orderClawPackageRemovals,
  normalizeClawPackageCleanup,
} from "./package-remove-plan.js";
import { applyClawPackageRemovals, type ClawPackageRemovalDecision } from "./package-remove.js";
import { readClawInstallRecord } from "./provenance.js";

export async function applyClawPackageRemovalPhase(
  decisions: ClawPackageRemovalDecision[],
  options: ClawRemoveApplyOptions & {
    agentId: string;
    operationId: string;
    assertCurrent: () => void;
  },
): Promise<ClawPackageRemovalPhaseResult> {
  const ordered = orderClawPackageRemovals(decisions);
  options.assertCurrent();
  const cleanup = normalizeClawPackageCleanup(
    filterReferencedCleanup(options.referencedCleanup, "package"),
  );
  if (
    !ordered.some(
      (decision) => decision.packageRef.kind === "plugin" && decision.action === "uninstall",
    )
  ) {
    return await applyClawPackageRemovals(ordered, { ...options, deps: options.packageDeps });
  }
  if (!options.packageGateway) {
    throw new Error("Plugin cleanup requires the serving Gateway package owner.");
  }
  // Hand off before taking either cross-process package mutation lease.
  const removed = await options.packageGateway({
    agentId: options.agentId,
    operationId: options.operationId,
    expectedInstallDigest: digestClawRemovalInstall(
      readClawInstallRecord(options.agentId, options),
    ),
    expectedPackagePlanDigest: digestClawPackageRemovalPlan(ordered, cleanup),
    cleanup,
  });
  options.assertCurrent();
  const expected = ordered.map(({ packageRef }) => [
    packageRef.kind,
    packageRef.ref,
    packageRef.version,
  ]);
  const actual = removed.packages.map((pkg) => [pkg.kind, pkg.ref, pkg.version]);
  if (
    !isDeepStrictEqual(expected, actual) ||
    (removed.packages.some((pkg) => pkg.kind === "plugin" && pkg.action === "uninstalled") &&
      !removed.application)
  ) {
    throw new Error("Gateway package cleanup returned incomplete removal outcomes.");
  }
  return removed;
}