@agentxm/extension-lifecycle 0.28.11 → 0.28.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/activation/display-paths.d.ts +25 -0
- package/dist/src/activation/display-paths.js +25 -0
- package/dist/src/activation/errors.d.ts +16 -0
- package/dist/src/activation/errors.js +7 -0
- package/dist/src/activation/set-activation.d.ts +134 -0
- package/dist/src/activation/set-activation.js +772 -0
- package/dist/src/demote/demote-to-external-source.d.ts +82 -0
- package/dist/src/demote/demote-to-external-source.js +233 -0
- package/dist/src/errors.d.ts +0 -8
- package/dist/src/errors.js +0 -8
- package/dist/src/glob.d.ts +35 -0
- package/dist/src/glob.js +75 -0
- package/dist/src/hooks/configured-agent-outcomes-provider.d.ts +7 -3
- package/dist/src/hooks/configured-agent-outcomes-provider.js +11 -4
- package/dist/src/hooks/install/plan.d.ts +45 -0
- package/dist/src/hooks/install/plan.js +224 -0
- package/dist/src/hooks/uninstall/plan.d.ts +20 -0
- package/dist/src/hooks/uninstall/plan.js +77 -0
- package/dist/src/index.d.ts +48 -16
- package/dist/src/index.js +60 -17
- package/dist/src/install/aggregate-projection-step.d.ts +16 -0
- package/dist/src/install/aggregate-projection-step.js +49 -0
- package/dist/src/install/configured.d.ts +65 -0
- package/dist/src/install/configured.js +406 -0
- package/dist/src/install/inline-mcp-operation.d.ts +4 -0
- package/dist/src/install/inline-mcp-operation.js +25 -0
- package/dist/src/install/install-extensions.d.ts +110 -0
- package/dist/src/install/install-extensions.js +282 -0
- package/dist/src/install/per-type-install.d.ts +12 -0
- package/dist/src/install/per-type-install.js +12 -0
- package/dist/src/install/registry-install-target.d.ts +35 -0
- package/dist/src/install/registry-install-target.js +66 -0
- package/dist/src/install/registry-login-suggestion.d.ts +21 -0
- package/dist/src/install/registry-login-suggestion.js +34 -0
- package/dist/src/install/registry-source-resolution.d.ts +54 -0
- package/dist/src/install/registry-source-resolution.js +216 -0
- package/dist/src/install/root-intent.d.ts +19 -0
- package/dist/src/install/root-intent.js +122 -0
- package/dist/src/install/selection-interaction.d.ts +46 -0
- package/dist/src/install/selection-interaction.js +22 -0
- package/dist/src/install/vocabulary.d.ts +143 -0
- package/dist/src/install/vocabulary.js +29 -0
- package/dist/src/knowledge/install/plan.d.ts +35 -0
- package/dist/src/knowledge/install/plan.js +128 -0
- package/dist/src/knowledge/uninstall/plan.d.ts +23 -0
- package/dist/src/knowledge/uninstall/plan.js +140 -0
- package/dist/src/live.d.ts +4 -11
- package/dist/src/live.js +4 -11
- package/dist/src/mcps/install/plan.d.ts +65 -0
- package/dist/src/mcps/install/plan.js +301 -0
- package/dist/src/mcps/operations/disable.d.ts +5 -3
- package/dist/src/mcps/operations/disable.js +6 -4
- package/dist/src/mcps/operations/enable.d.ts +5 -3
- package/dist/src/mcps/operations/enable.js +8 -6
- package/dist/src/mcps/operations/sync-outcome.d.ts +1 -1
- package/dist/src/mcps/operations/uninstall.d.ts +5 -3
- package/dist/src/mcps/operations/uninstall.js +4 -4
- package/dist/src/mcps/uninstall/plan.d.ts +24 -0
- package/dist/src/mcps/uninstall/plan.js +109 -0
- package/dist/src/packs/artifact.d.ts +21 -0
- package/dist/src/packs/artifact.js +32 -0
- package/dist/src/packs/constraint-gate.d.ts +30 -0
- package/dist/src/packs/constraint-gate.js +76 -0
- package/dist/src/packs/expansion.d.ts +13 -14
- package/dist/src/packs/expansion.js +1 -2
- package/dist/src/packs/graph-transition.d.ts +64 -0
- package/dist/src/packs/graph-transition.js +219 -0
- package/dist/src/packs/install/plan.d.ts +95 -0
- package/dist/src/packs/install/plan.js +845 -0
- package/dist/src/packs/member-install-step.d.ts +29 -0
- package/dist/src/packs/member-install-step.js +156 -0
- package/dist/src/packs/operations/install.d.ts +3 -3
- package/dist/src/packs/operations/install.js +5 -5
- package/dist/src/packs/promote-authored-pack.d.ts +86 -0
- package/dist/src/packs/promote-authored-pack.js +239 -0
- package/dist/src/packs/resolved-dependency.d.ts +9 -26
- package/dist/src/packs/resolved-dependency.js +8 -20
- package/dist/src/packs/uninstall/plan.d.ts +66 -0
- package/dist/src/packs/uninstall/plan.js +344 -0
- package/dist/src/packs/uninstall/readiness.d.ts +50 -0
- package/dist/src/packs/uninstall/readiness.js +130 -0
- package/dist/src/publisher-binding.d.ts +8 -40
- package/dist/src/publisher-binding.js +8 -46
- package/dist/src/rules/install/plan.d.ts +35 -0
- package/dist/src/rules/install/plan.js +133 -0
- package/dist/src/rules/uninstall/plan.d.ts +19 -0
- package/dist/src/rules/uninstall/plan.js +54 -0
- package/dist/src/skills/install/bundled.d.ts +73 -0
- package/dist/src/skills/install/bundled.js +244 -0
- package/dist/src/skills/install/plan.d.ts +72 -0
- package/dist/src/skills/install/plan.js +429 -0
- package/dist/src/skills/install/selection.d.ts +26 -0
- package/dist/src/skills/install/selection.js +41 -0
- package/dist/src/skills/install/source.d.ts +21 -0
- package/dist/src/skills/install/source.js +76 -0
- package/dist/src/skills/operations/disable.d.ts +3 -2
- package/dist/src/skills/operations/disable.js +7 -11
- package/dist/src/skills/operations/enable.d.ts +3 -2
- package/dist/src/skills/operations/enable.js +6 -8
- package/dist/src/skills/operations/install.d.ts +4 -4
- package/dist/src/skills/operations/install.js +13 -12
- package/dist/src/skills/uninstall/plan.d.ts +23 -0
- package/dist/src/skills/uninstall/plan.js +165 -0
- package/dist/src/skills/utils.js +1 -1
- package/dist/src/step-failure-conversion.d.ts +44 -0
- package/dist/src/step-failure-conversion.js +26 -0
- package/dist/src/step-failure.d.ts +29 -0
- package/dist/src/step-failure.js +130 -0
- package/dist/src/subagents/install/plan.d.ts +49 -0
- package/dist/src/subagents/install/plan.js +270 -0
- package/dist/src/subagents/install/selection.d.ts +26 -0
- package/dist/src/subagents/install/selection.js +37 -0
- package/dist/src/subagents/install/source.d.ts +18 -0
- package/dist/src/subagents/install/source.js +72 -0
- package/dist/src/subagents/operations/disable.d.ts +5 -3
- package/dist/src/subagents/operations/disable.js +5 -3
- package/dist/src/subagents/operations/enable.d.ts +5 -3
- package/dist/src/subagents/operations/enable.js +17 -12
- package/dist/src/subagents/uninstall/plan.d.ts +20 -0
- package/dist/src/subagents/uninstall/plan.js +142 -0
- package/dist/src/test-packages.d.ts +34 -0
- package/dist/src/test-packages.js +111 -0
- package/dist/src/test-registry.d.ts +78 -0
- package/dist/src/test-registry.js +223 -0
- package/dist/src/testing.d.ts +108 -0
- package/dist/src/testing.js +241 -0
- package/dist/src/uninstall/retention-policy.d.ts +21 -0
- package/dist/src/uninstall/retention-policy.js +28 -0
- package/dist/src/uninstall/root-intent.d.ts +17 -0
- package/dist/src/uninstall/root-intent.js +101 -0
- package/dist/src/uninstall/uninstall-extensions.d.ts +61 -0
- package/dist/src/uninstall/uninstall-extensions.js +127 -0
- package/dist/src/uninstall/vocabulary.d.ts +40 -0
- package/dist/src/uninstall/vocabulary.js +14 -0
- package/dist/src/update/atomicity.d.ts +22 -0
- package/dist/src/update/atomicity.js +23 -0
- package/dist/src/update/blockers.d.ts +31 -0
- package/dist/src/update/blockers.js +78 -0
- package/dist/src/update/configured.d.ts +78 -0
- package/dist/src/update/configured.js +546 -0
- package/dist/src/update/root-request.d.ts +39 -0
- package/dist/src/update/root-request.js +117 -0
- package/dist/src/update/selective/plan.d.ts +45 -0
- package/dist/src/update/selective/plan.js +61 -0
- package/dist/src/update/selective/requirements.d.ts +17 -0
- package/dist/src/update/selective/requirements.js +14 -0
- package/dist/src/update/selective/selection.d.ts +63 -0
- package/dist/src/update/selective/selection.js +85 -0
- package/dist/src/update/selective/skills.d.ts +55 -0
- package/dist/src/update/selective/skills.js +596 -0
- package/dist/src/update/selective/subagents.d.ts +52 -0
- package/dist/src/update/selective/subagents.js +331 -0
- package/dist/src/update/selective/use-case.d.ts +121 -0
- package/dist/src/update/selective/use-case.js +28 -0
- package/dist/src/update/selective/vocabulary.d.ts +43 -0
- package/dist/src/update/selective/vocabulary.js +28 -0
- package/dist/src/update/selector.d.ts +72 -0
- package/dist/src/update/selector.js +120 -0
- package/dist/src/update/targeted-plan.d.ts +30 -0
- package/dist/src/update/targeted-plan.js +113 -0
- package/dist/src/update/update-extensions.d.ts +164 -0
- package/dist/src/update/update-extensions.js +579 -0
- package/dist/src/workspace-paths.d.ts +31 -0
- package/dist/src/workspace-paths.js +46 -0
- package/package.json +22 -12
- package/dist/src/configured-entry-resolution.d.ts +0 -111
- package/dist/src/configured-entry-resolution.js +0 -731
- package/dist/src/failure-adapter.d.ts +0 -35
- package/dist/src/failure-adapter.js +0 -23
- package/dist/src/hooks/manager.d.ts +0 -15
- package/dist/src/hooks/manager.js +0 -769
- package/dist/src/internal/fs-helpers.d.ts +0 -12
- package/dist/src/internal/fs-helpers.js +0 -13
- package/dist/src/internal/integrity.d.ts +0 -13
- package/dist/src/internal/integrity.js +0 -17
- package/dist/src/knowledge/manager.d.ts +0 -11
- package/dist/src/knowledge/manager.js +0 -711
- package/dist/src/mcps/manager.d.ts +0 -16
- package/dist/src/mcps/manager.js +0 -247
- package/dist/src/mcps/operations/artifact.d.ts +0 -24
- package/dist/src/mcps/operations/artifact.js +0 -74
- package/dist/src/mcps/operations/install.d.ts +0 -78
- package/dist/src/mcps/operations/install.js +0 -596
- package/dist/src/packs/dependency-resolution.d.ts +0 -65
- package/dist/src/packs/dependency-resolution.js +0 -414
- package/dist/src/packs/manager.d.ts +0 -16
- package/dist/src/packs/manager.js +0 -181
- package/dist/src/registry-materialization.d.ts +0 -52
- package/dist/src/registry-materialization.js +0 -72
- package/dist/src/release-age-posture.d.ts +0 -23
- package/dist/src/release-age-posture.js +0 -15
- package/dist/src/resolution-timeout.d.ts +0 -5
- package/dist/src/resolution-timeout.js +0 -11
- package/dist/src/rules/manager.d.ts +0 -20
- package/dist/src/rules/manager.js +0 -480
- package/dist/src/skills/manager.d.ts +0 -18
- package/dist/src/skills/manager.js +0 -346
- package/dist/src/skills/materialization.d.ts +0 -54
- package/dist/src/skills/materialization.js +0 -158
- package/dist/src/skills/operations/source-hash.d.ts +0 -2
- package/dist/src/skills/operations/source-hash.js +0 -6
- package/dist/src/subagents/manager.d.ts +0 -18
- package/dist/src/subagents/manager.js +0 -722
- package/dist/src/workflows/install-command/workflow.d.ts +0 -56
- package/dist/src/workflows/install-command/workflow.js +0 -46
- package/dist/src/workflows/uninstall-command/workflow.d.ts +0 -42
- package/dist/src/workflows/uninstall-command/workflow.js +0 -29
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uninstalling packs.
|
|
3
|
+
*
|
|
4
|
+
* Removing a pack removes every member no other origin still declares, in one
|
|
5
|
+
* transition: members first, while their pack-derived desired state is still
|
|
6
|
+
* observable, then the pack itself. A selected pack whose own manifest cannot
|
|
7
|
+
* be read is retired — its registration goes, its unverifiable content stays —
|
|
8
|
+
* and a graph incomplete for any other reason blocks instead.
|
|
9
|
+
*
|
|
10
|
+
* @experimental This API is unstable and may change without notice.
|
|
11
|
+
*/
|
|
12
|
+
import * as Effect from "effect/Effect";
|
|
13
|
+
import * as FileSystem from "effect/FileSystem";
|
|
14
|
+
import * as Path from "effect/Path";
|
|
15
|
+
import { HookManager, KnowledgeManager, McpServerManager, PackManager, RuleManager, SkillManager, SubagentManager } from "@agentxm/extension-materialization";
|
|
16
|
+
import { type ExtensionFqnParts, type ExtensionName, type Handle } from "@agentxm/extension-model/unstable/extensions";
|
|
17
|
+
import { type Plan } from "@agentxm/workspace-operations";
|
|
18
|
+
import { type DesiredPackageAuthority, type DesiredStateGraph, type PackExtensionTarget } from "@agentxm/workspace-state";
|
|
19
|
+
import type { ExtensionLifecycleFailed } from "../../errors.js";
|
|
20
|
+
import { type InstallStepRequirements } from "../../install/vocabulary.js";
|
|
21
|
+
import { type PackRetirement } from "./readiness.js";
|
|
22
|
+
/** How the request named the pack to remove. */
|
|
23
|
+
export type PackUninstallSelector = {
|
|
24
|
+
readonly _tag: "ExactFqn";
|
|
25
|
+
readonly identity: ExtensionFqnParts & {
|
|
26
|
+
readonly type: "pack";
|
|
27
|
+
};
|
|
28
|
+
} | {
|
|
29
|
+
readonly _tag: "SimpleName";
|
|
30
|
+
readonly name: string;
|
|
31
|
+
};
|
|
32
|
+
/** One pack the removal resolved, with the desired identity it was resolved from. */
|
|
33
|
+
export interface ResolvedPackUninstallTarget extends PackExtensionTarget {
|
|
34
|
+
readonly owner: Handle;
|
|
35
|
+
readonly name: ExtensionName;
|
|
36
|
+
readonly authority: DesiredPackageAuthority;
|
|
37
|
+
readonly desiredIdentity: string;
|
|
38
|
+
}
|
|
39
|
+
/** The packs this removal withdraws. */
|
|
40
|
+
export interface PackUninstallIntent {
|
|
41
|
+
readonly packsToUninstall: ReadonlyArray<ResolvedPackUninstallTarget>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The pack this candidate was resolved from must still be the pack in the
|
|
45
|
+
* graph when the transition applies; otherwise the candidate describes a
|
|
46
|
+
* decision that is no longer available.
|
|
47
|
+
*/
|
|
48
|
+
export declare const validateResolvedPackUninstallTargets: (graph: DesiredStateGraph, targets: ReadonlyArray<ResolvedPackUninstallTarget>) => Effect.Effect<void, ExtensionLifecycleFailed>;
|
|
49
|
+
/**
|
|
50
|
+
* Preview and apply must reach one decision from unchanged inputs. A target
|
|
51
|
+
* whose package became readable — or unreadable — between the two phases is a
|
|
52
|
+
* different decision, so the candidate is stale rather than applicable.
|
|
53
|
+
*/
|
|
54
|
+
export declare const validatePackRetirementFacts: (args: {
|
|
55
|
+
readonly planned: ReadonlyArray<PackRetirement>;
|
|
56
|
+
readonly observed: ReadonlyArray<PackRetirement> | undefined;
|
|
57
|
+
}) => Effect.Effect<void, ExtensionLifecycleFailed>;
|
|
58
|
+
/** Read the pack selector: an exact FQN, one name, or a glob over pack names. */
|
|
59
|
+
export declare const parsePackUninstallSelectors: (selector: string) => Effect.Effect<ReadonlyArray<PackUninstallSelector>, ExtensionLifecycleFailed, InstallStepRequirements>;
|
|
60
|
+
/** Resolve the selectors against the desired graph. */
|
|
61
|
+
export declare const finalizePackUninstallIntent: (selectors: ReadonlyArray<PackUninstallSelector>) => Effect.Effect<PackUninstallIntent, ExtensionLifecycleFailed, InstallStepRequirements>;
|
|
62
|
+
/** Everything the pack removal reads and writes through. */
|
|
63
|
+
export type PackUninstallRequirements = InstallStepRequirements | Path.Path | FileSystem.FileSystem | HookManager | KnowledgeManager | McpServerManager | PackManager | RuleManager | SkillManager | SubagentManager;
|
|
64
|
+
/** The single atomic closure a settled pack removal becomes. */
|
|
65
|
+
export declare const planPackUninstall: (intent: PackUninstallIntent) => Effect.Effect<Plan<InstallStepRequirements>, ExtensionLifecycleFailed, PackUninstallRequirements>;
|
|
66
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uninstalling packs.
|
|
3
|
+
*
|
|
4
|
+
* Removing a pack removes every member no other origin still declares, in one
|
|
5
|
+
* transition: members first, while their pack-derived desired state is still
|
|
6
|
+
* observable, then the pack itself. A selected pack whose own manifest cannot
|
|
7
|
+
* be read is retired — its registration goes, its unverifiable content stays —
|
|
8
|
+
* and a graph incomplete for any other reason blocks instead.
|
|
9
|
+
*
|
|
10
|
+
* @experimental This API is unstable and may change without notice.
|
|
11
|
+
*/
|
|
12
|
+
import * as Effect from "effect/Effect";
|
|
13
|
+
import * as FileSystem from "effect/FileSystem";
|
|
14
|
+
import * as Option from "effect/Option";
|
|
15
|
+
import * as Path from "effect/Path";
|
|
16
|
+
import { HookManager, KnowledgeManager, McpServerManager, PackManager, RuleManager, SkillManager, SubagentManager, buildUninstallOperation, toLabel, } from "@agentxm/extension-materialization";
|
|
17
|
+
import { parseExtensionFqnParts, } from "@agentxm/extension-model/unstable/extensions";
|
|
18
|
+
import { operationPresentation, } from "@agentxm/workspace-operations";
|
|
19
|
+
import { WorkspaceMutations, decodeDesiredExtensionIdentity, } from "@agentxm/workspace-state";
|
|
20
|
+
import { expandGlob } from "../../glob.js";
|
|
21
|
+
import { lifecycleStepFailure } from "../../step-failure.js";
|
|
22
|
+
import { buildAggregateProjectionStep } from "../../install/aggregate-projection-step.js";
|
|
23
|
+
import { installRefused } from "../../install/vocabulary.js";
|
|
24
|
+
import { exclusiveMemberRetentionPolicy, makeWorkspaceRetentionPolicy, } from "../../uninstall/retention-policy.js";
|
|
25
|
+
import { workspaceCanonicalNodePath } from "../../workspace-paths.js";
|
|
26
|
+
import { buildAtomicPackGraphStep, validatePackGraphPostcondition } from "../graph-transition.js";
|
|
27
|
+
import { PACK_UNINSTALL_GRAPH_BLOCKER_ID, planPackUninstallGraphReadiness, } from "./readiness.js";
|
|
28
|
+
const normalizedPackIdentity = (identity) => identity.startsWith("workspace:") ? identity.slice("workspace:".length) : identity;
|
|
29
|
+
const identityValidationError = (identity) => installRefused({
|
|
30
|
+
category: "validation",
|
|
31
|
+
detail: `Configured pack identity ${identity} is invalid`,
|
|
32
|
+
});
|
|
33
|
+
const readDesiredGraph = Effect.gen(function* () {
|
|
34
|
+
const ws = yield* WorkspaceMutations;
|
|
35
|
+
return yield* ws
|
|
36
|
+
.getDesiredStateGraph()
|
|
37
|
+
.pipe(Effect.mapError((cause) => installRefused({ category: "internal", detail: "Desired state could not be read", cause })));
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* The pack this candidate was resolved from must still be the pack in the
|
|
41
|
+
* graph when the transition applies; otherwise the candidate describes a
|
|
42
|
+
* decision that is no longer available.
|
|
43
|
+
*/
|
|
44
|
+
export const validateResolvedPackUninstallTargets = (graph, targets) => Effect.gen(function* () {
|
|
45
|
+
for (const expected of targets) {
|
|
46
|
+
const current = graph.nodes.find((candidate) => candidate.type === "pack" && candidate.name === expected.name);
|
|
47
|
+
if (current === undefined) {
|
|
48
|
+
return yield* installRefused({
|
|
49
|
+
category: "conflict",
|
|
50
|
+
detail: `Pack ${expected.desiredIdentity} changed or was removed before uninstall`,
|
|
51
|
+
recover: "Inspect the current pack state, then retry the uninstall.",
|
|
52
|
+
cmd: `axm packs show ${expected.name}`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const decoded = decodeDesiredExtensionIdentity(current.identity);
|
|
56
|
+
if (decoded === undefined || decoded.type !== "pack") {
|
|
57
|
+
return yield* identityValidationError(current.identity);
|
|
58
|
+
}
|
|
59
|
+
if (current.identity !== expected.desiredIdentity ||
|
|
60
|
+
decoded.authority !== expected.authority ||
|
|
61
|
+
decoded.owner !== expected.owner ||
|
|
62
|
+
decoded.name !== expected.name) {
|
|
63
|
+
return yield* installRefused({
|
|
64
|
+
category: "conflict",
|
|
65
|
+
detail: `Pack ${expected.desiredIdentity} changed or was removed before uninstall`,
|
|
66
|
+
recover: "Inspect the current pack state, then retry the uninstall.",
|
|
67
|
+
cmd: `axm packs show ${expected.name}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
const retirementKey = (retirement) => `${retirement.pack}\u0000${retirement.reason}\u0000${retirement.manifestPath}`;
|
|
73
|
+
const retirementSetKey = (retirements) => [...retirements].map(retirementKey).sort().join("\u0001");
|
|
74
|
+
/**
|
|
75
|
+
* Preview and apply must reach one decision from unchanged inputs. A target
|
|
76
|
+
* whose package became readable — or unreadable — between the two phases is a
|
|
77
|
+
* different decision, so the candidate is stale rather than applicable.
|
|
78
|
+
*/
|
|
79
|
+
export const validatePackRetirementFacts = (args) => {
|
|
80
|
+
if (args.observed !== undefined &&
|
|
81
|
+
retirementSetKey(args.observed) === retirementSetKey(args.planned)) {
|
|
82
|
+
return Effect.void;
|
|
83
|
+
}
|
|
84
|
+
const packs = [...new Set([...args.planned].map((retirement) => retirement.pack))];
|
|
85
|
+
return Effect.fail(installRefused({
|
|
86
|
+
category: "conflict",
|
|
87
|
+
detail: packs.length === 0
|
|
88
|
+
? "The pack graph changed before uninstall"
|
|
89
|
+
: `Pack package readability for ${packs.join(", ")} changed before uninstall`,
|
|
90
|
+
recover: "Inspect the current pack state, then retry the uninstall.",
|
|
91
|
+
cmd: "axm packs list",
|
|
92
|
+
}));
|
|
93
|
+
};
|
|
94
|
+
const uninstallPresentation = operationPresentation({ imperative: "uninstall", past: "Uninstalled", gerund: "Uninstalling" }, "pack");
|
|
95
|
+
/** Read the pack selector: an exact FQN, one name, or a glob over pack names. */
|
|
96
|
+
export const parsePackUninstallSelectors = Effect.fn("UninstallExtensions.parsePackSelectors")(function* (selector) {
|
|
97
|
+
const requested = parseExtensionFqnParts(selector);
|
|
98
|
+
if (requested !== undefined && requested.type !== "pack") {
|
|
99
|
+
return yield* installRefused({
|
|
100
|
+
category: "validation",
|
|
101
|
+
detail: `Expected a pack identity, received ${selector}`,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (requested !== undefined) {
|
|
105
|
+
return [
|
|
106
|
+
{
|
|
107
|
+
_tag: "ExactFqn",
|
|
108
|
+
identity: { owner: requested.owner, type: "pack", name: requested.name },
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
if (!selector.includes("*"))
|
|
113
|
+
return [{ _tag: "SimpleName", name: selector }];
|
|
114
|
+
const graph = yield* readDesiredGraph;
|
|
115
|
+
const names = expandGlob(selector, graph.nodes.filter((node) => node.type === "pack").map((node) => node.name));
|
|
116
|
+
return names.map((name) => ({ _tag: "SimpleName", name }));
|
|
117
|
+
});
|
|
118
|
+
/** Resolve the selectors against the desired graph. */
|
|
119
|
+
export const finalizePackUninstallIntent = Effect.fn("UninstallExtensions.finalizePackIntent")(function* (selectors) {
|
|
120
|
+
const graph = yield* readDesiredGraph;
|
|
121
|
+
const targets = new Map();
|
|
122
|
+
for (const selector of selectors) {
|
|
123
|
+
const name = selector._tag === "ExactFqn" ? selector.identity.name : selector.name;
|
|
124
|
+
const candidates = graph.nodes.filter((candidate) => candidate.type === "pack" && candidate.name === name);
|
|
125
|
+
for (const node of candidates) {
|
|
126
|
+
const identity = decodeDesiredExtensionIdentity(node.identity);
|
|
127
|
+
if (identity === undefined || identity.type !== "pack") {
|
|
128
|
+
return yield* identityValidationError(node.identity);
|
|
129
|
+
}
|
|
130
|
+
if (selector._tag === "ExactFqn" && identity.owner !== selector.identity.owner)
|
|
131
|
+
continue;
|
|
132
|
+
targets.set(node.identity, {
|
|
133
|
+
type: "pack",
|
|
134
|
+
name: identity.name,
|
|
135
|
+
owner: identity.owner,
|
|
136
|
+
authority: identity.authority,
|
|
137
|
+
desiredIdentity: node.identity,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { packsToUninstall: [...targets.values()] };
|
|
142
|
+
});
|
|
143
|
+
/** The single atomic closure a settled pack removal becomes. */
|
|
144
|
+
export const planPackUninstall = Effect.fn("UninstallExtensions.planPacks")(function* (intent) {
|
|
145
|
+
const ws = yield* WorkspaceMutations;
|
|
146
|
+
const path = yield* Path.Path;
|
|
147
|
+
const packManager = yield* PackManager;
|
|
148
|
+
const skillManager = yield* SkillManager;
|
|
149
|
+
const subagentManager = yield* SubagentManager;
|
|
150
|
+
const ruleManager = yield* RuleManager;
|
|
151
|
+
const hookManager = yield* HookManager;
|
|
152
|
+
const knowledgeManager = yield* KnowledgeManager;
|
|
153
|
+
const mcpServerManager = yield* McpServerManager;
|
|
154
|
+
const observedGraph = yield* readDesiredGraph;
|
|
155
|
+
const graphReadiness = planPackUninstallGraphReadiness(observedGraph, intent.packsToUninstall.map((pack) => pack.desiredIdentity), ws.scope);
|
|
156
|
+
if (graphReadiness.readiness === "blocked") {
|
|
157
|
+
return {
|
|
158
|
+
_tag: "Plan",
|
|
159
|
+
name: "Uninstall packs",
|
|
160
|
+
description: Option.some("Pack graph readiness prevents this uninstall."),
|
|
161
|
+
presentation: uninstallPresentation,
|
|
162
|
+
jobs: [
|
|
163
|
+
{
|
|
164
|
+
concurrency: 1,
|
|
165
|
+
steps: [
|
|
166
|
+
{
|
|
167
|
+
readiness: "error",
|
|
168
|
+
label: intent.packsToUninstall.map((pack) => pack.desiredIdentity).join(", ") ||
|
|
169
|
+
"Pack graph",
|
|
170
|
+
errorMessage: graphReadiness.detail,
|
|
171
|
+
blockingConditionIds: [PACK_UNINSTALL_GRAPH_BLOCKER_ID],
|
|
172
|
+
artifact: {
|
|
173
|
+
path: "pack graph",
|
|
174
|
+
scope: ws.scope,
|
|
175
|
+
change: "unchanged",
|
|
176
|
+
fileCount: 0,
|
|
177
|
+
targets: graphReadiness.facts.flatMap((fact) => fact.authoritativeLocations.map((location) => ({
|
|
178
|
+
path: location,
|
|
179
|
+
change: "unchanged",
|
|
180
|
+
}))),
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
riskConditions: [
|
|
187
|
+
{
|
|
188
|
+
level: "blocked",
|
|
189
|
+
id: PACK_UNINSTALL_GRAPH_BLOCKER_ID,
|
|
190
|
+
detail: graphReadiness.detail,
|
|
191
|
+
errorCode: "conflict",
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (intent.packsToUninstall.length === 0) {
|
|
197
|
+
return {
|
|
198
|
+
_tag: "Plan",
|
|
199
|
+
name: "Uninstall packs",
|
|
200
|
+
description: Option.none(),
|
|
201
|
+
presentation: uninstallPresentation,
|
|
202
|
+
jobs: [{ concurrency: 1, steps: [] }],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const graph = graphReadiness.graph;
|
|
206
|
+
const plannedRetirements = graphReadiness.retirements;
|
|
207
|
+
const retirementByIdentity = new Map(plannedRetirements.map((retirement) => [retirement.pack, retirement]));
|
|
208
|
+
// Only a selected pack can be retired, and members are never read from an
|
|
209
|
+
// unreadable manifest, so the lookup is keyed by the selected pack's name.
|
|
210
|
+
const retirementByPackName = new Map(intent.packsToUninstall.flatMap((pack) => {
|
|
211
|
+
const retirement = retirementByIdentity.get(normalizedPackIdentity(pack.desiredIdentity));
|
|
212
|
+
return retirement === undefined ? [] : [[pack.name, retirement]];
|
|
213
|
+
}));
|
|
214
|
+
const retentionPolicy = makeWorkspaceRetentionPolicy(ws);
|
|
215
|
+
const allTargets = new Map();
|
|
216
|
+
for (const pack of intent.packsToUninstall) {
|
|
217
|
+
allTargets.set(`pack:${pack.name}`, pack);
|
|
218
|
+
}
|
|
219
|
+
const removingPackIdentities = new Set(intent.packsToUninstall.map((pack) => pack.desiredIdentity));
|
|
220
|
+
for (const node of graph.nodes) {
|
|
221
|
+
if (node.type === "pack")
|
|
222
|
+
continue;
|
|
223
|
+
const removedOrigin = node.origins.some((origin) => origin.type === "pack" && removingPackIdentities.has(origin.pack));
|
|
224
|
+
if (!removedOrigin)
|
|
225
|
+
continue;
|
|
226
|
+
const retainedOrigin = node.origins.some((origin) => origin.type === "settings" ||
|
|
227
|
+
(origin.type === "pack" && !removingPackIdentities.has(origin.pack)));
|
|
228
|
+
if (retainedOrigin)
|
|
229
|
+
continue;
|
|
230
|
+
allTargets.set(`${node.type}:${node.name}`, { type: node.type, name: node.name });
|
|
231
|
+
}
|
|
232
|
+
// Remove members while their pack-derived desired state is still
|
|
233
|
+
// observable, then retire the owning pack.
|
|
234
|
+
const packTargets = [...allTargets.values()].filter((target) => target.type === "pack");
|
|
235
|
+
const depTargets = [...allTargets.values()].filter((target) => target.type !== "pack");
|
|
236
|
+
const orderedTargets = [...depTargets, ...packTargets];
|
|
237
|
+
const sourcePathByTarget = new Map(graph.nodes.map((node) => [
|
|
238
|
+
`${node.type}:${node.name}`,
|
|
239
|
+
workspaceCanonicalNodePath(path, ws, node),
|
|
240
|
+
]));
|
|
241
|
+
const steps = orderedTargets.map((target) => {
|
|
242
|
+
switch (target.type) {
|
|
243
|
+
case "pack": {
|
|
244
|
+
const retirement = retirementByPackName.get(target.name);
|
|
245
|
+
return buildUninstallOperation(packManager, retentionPolicy, {
|
|
246
|
+
target,
|
|
247
|
+
toStepFailure: lifecycleStepFailure,
|
|
248
|
+
...(retirement === undefined
|
|
249
|
+
? {}
|
|
250
|
+
: {
|
|
251
|
+
retirement: {
|
|
252
|
+
// Results name workspace-relative paths, so the same
|
|
253
|
+
// retirement reads identically from any workspace.
|
|
254
|
+
manifestPath: path.relative(ws.baseDir, retirement.manifestPath),
|
|
255
|
+
reason: retirement.reason,
|
|
256
|
+
},
|
|
257
|
+
}),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
case "skill":
|
|
261
|
+
return buildUninstallOperation(skillManager, exclusiveMemberRetentionPolicy, {
|
|
262
|
+
toStepFailure: lifecycleStepFailure,
|
|
263
|
+
target,
|
|
264
|
+
});
|
|
265
|
+
case "mcp-server":
|
|
266
|
+
return buildUninstallOperation(mcpServerManager, exclusiveMemberRetentionPolicy, {
|
|
267
|
+
toStepFailure: lifecycleStepFailure,
|
|
268
|
+
target,
|
|
269
|
+
});
|
|
270
|
+
case "subagent":
|
|
271
|
+
return buildUninstallOperation(subagentManager, exclusiveMemberRetentionPolicy, {
|
|
272
|
+
toStepFailure: lifecycleStepFailure,
|
|
273
|
+
target,
|
|
274
|
+
});
|
|
275
|
+
case "rule":
|
|
276
|
+
return buildUninstallOperation(ruleManager, exclusiveMemberRetentionPolicy, {
|
|
277
|
+
toStepFailure: lifecycleStepFailure,
|
|
278
|
+
target,
|
|
279
|
+
skipProjections: true,
|
|
280
|
+
});
|
|
281
|
+
case "hook":
|
|
282
|
+
return buildUninstallOperation(hookManager, exclusiveMemberRetentionPolicy, {
|
|
283
|
+
toStepFailure: lifecycleStepFailure,
|
|
284
|
+
target,
|
|
285
|
+
skipProjections: true,
|
|
286
|
+
});
|
|
287
|
+
case "knowledge":
|
|
288
|
+
return buildUninstallOperation(knowledgeManager, exclusiveMemberRetentionPolicy, {
|
|
289
|
+
toStepFailure: lifecycleStepFailure,
|
|
290
|
+
target,
|
|
291
|
+
skipProjections: true,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
const projectionStep = yield* buildAggregateProjectionStep({
|
|
296
|
+
types: new Set(orderedTargets.map((target) => target.type)),
|
|
297
|
+
});
|
|
298
|
+
const registrationOnly = plannedRetirements.length === 0
|
|
299
|
+
? ""
|
|
300
|
+
: ` (${plannedRetirements.length} pack${plannedRetirements.length === 1 ? "" : "s"} unregistered without removing package content)`;
|
|
301
|
+
const graphStep = yield* buildAtomicPackGraphStep({
|
|
302
|
+
label: `${intent.packsToUninstall.length} pack${intent.packsToUninstall.length === 1 ? "" : "s"}`,
|
|
303
|
+
message: `Uninstalled ${intent.packsToUninstall.length} pack${intent.packsToUninstall.length === 1 ? "" : "s"} and ${depTargets.length} exclusive member${depTargets.length === 1 ? "" : "s"}${registrationOnly}`,
|
|
304
|
+
artifact: {
|
|
305
|
+
path: "pack graph",
|
|
306
|
+
scope: ws.scope,
|
|
307
|
+
change: "removed",
|
|
308
|
+
fileCount: orderedTargets.length,
|
|
309
|
+
targets: orderedTargets.map((target) => ({
|
|
310
|
+
path: sourcePathByTarget.get(`${target.type}:${target.name}`) ?? toLabel(target),
|
|
311
|
+
// Content AXM could not verify is preserved, so its canonical path is
|
|
312
|
+
// never reported as removed.
|
|
313
|
+
change: target.type === "pack" && retirementByPackName.has(target.name) ? "unchanged" : "removed",
|
|
314
|
+
})),
|
|
315
|
+
},
|
|
316
|
+
children: [
|
|
317
|
+
...steps.map((step) => ({ step, coverage: "ineligible" })),
|
|
318
|
+
...Option.toArray(projectionStep).map((step) => ({
|
|
319
|
+
step,
|
|
320
|
+
coverage: "ineligible",
|
|
321
|
+
})),
|
|
322
|
+
],
|
|
323
|
+
preTransition: Effect.gen(function* () {
|
|
324
|
+
const currentGraph = yield* readDesiredGraph;
|
|
325
|
+
yield* validateResolvedPackUninstallTargets(currentGraph, intent.packsToUninstall);
|
|
326
|
+
const currentReadiness = planPackUninstallGraphReadiness(currentGraph, intent.packsToUninstall.map((pack) => pack.desiredIdentity), ws.scope);
|
|
327
|
+
yield* validatePackRetirementFacts({
|
|
328
|
+
planned: plannedRetirements,
|
|
329
|
+
observed: currentReadiness.readiness === "ready" ? currentReadiness.retirements : undefined,
|
|
330
|
+
});
|
|
331
|
+
}),
|
|
332
|
+
validate: validatePackGraphPostcondition({ absent: orderedTargets }),
|
|
333
|
+
});
|
|
334
|
+
return {
|
|
335
|
+
_tag: "Plan",
|
|
336
|
+
name: intent.packsToUninstall.length === 1
|
|
337
|
+
? "Uninstall pack"
|
|
338
|
+
: `Uninstall ${intent.packsToUninstall.length} packs`,
|
|
339
|
+
description: Option.none(),
|
|
340
|
+
presentation: uninstallPresentation,
|
|
341
|
+
jobs: [{ concurrency: 1, steps: [graphStep] }],
|
|
342
|
+
};
|
|
343
|
+
});
|
|
344
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DesiredStateGraph, DesiredStateProblem } from "@agentxm/workspace-state";
|
|
2
|
+
import type { WorkspaceScope } from "@agentxm/extension-model/unstable/workspace-scope";
|
|
3
|
+
/** Recovery-conformance identity for Pack uninstall planning on an incomplete graph. */
|
|
4
|
+
export declare const PACK_UNINSTALL_GRAPH_BLOCKER_ID: "packs/uninstall/desired-state-graph-complete";
|
|
5
|
+
/** Executable Pack-uninstall blocker identities covered by recovery conformance. */
|
|
6
|
+
export declare const packUninstallRecoveryIdentifiers: readonly ["packs/uninstall/desired-state-graph-complete"];
|
|
7
|
+
export interface PackUninstallGraphBlockerFact {
|
|
8
|
+
readonly problemType: DesiredStateProblem["type"] | "unknown";
|
|
9
|
+
readonly packs: ReadonlyArray<string>;
|
|
10
|
+
readonly member?: {
|
|
11
|
+
readonly type: string;
|
|
12
|
+
readonly name: string;
|
|
13
|
+
};
|
|
14
|
+
readonly authoritativeLocations: ReadonlyArray<string>;
|
|
15
|
+
readonly detail: string;
|
|
16
|
+
}
|
|
17
|
+
/** Why a selected Pack's own package could not be read. */
|
|
18
|
+
export type PackRetirementReason = "missing" | "invalid";
|
|
19
|
+
/**
|
|
20
|
+
* A selected Pack whose own package manifest is unreadable. Its registration is
|
|
21
|
+
* removable from positive evidence about the Pack itself; its content is not,
|
|
22
|
+
* because nothing about that content can be verified.
|
|
23
|
+
*/
|
|
24
|
+
export interface PackRetirement {
|
|
25
|
+
readonly pack: string;
|
|
26
|
+
readonly manifestPath: string;
|
|
27
|
+
readonly reason: PackRetirementReason;
|
|
28
|
+
}
|
|
29
|
+
export type PackUninstallGraphReadiness = {
|
|
30
|
+
readonly readiness: "ready";
|
|
31
|
+
readonly graph: DesiredStateGraph;
|
|
32
|
+
readonly retirements: ReadonlyArray<PackRetirement>;
|
|
33
|
+
} | {
|
|
34
|
+
readonly readiness: "blocked";
|
|
35
|
+
readonly id: typeof PACK_UNINSTALL_GRAPH_BLOCKER_ID;
|
|
36
|
+
readonly facts: ReadonlyArray<PackUninstallGraphBlockerFact>;
|
|
37
|
+
readonly detail: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Build the Pack-uninstall readiness decision from the shared desired-state
|
|
41
|
+
* planner.
|
|
42
|
+
*
|
|
43
|
+
* The graph gate exists to stop a Pack transition computed from state AXM
|
|
44
|
+
* cannot read. A selected Pack whose own manifest is confirmed missing or
|
|
45
|
+
* undecodable is positive evidence about the removal target, not the absent
|
|
46
|
+
* evidence the gate guards against, so uninstall proceeds and removes only that
|
|
47
|
+
* Pack's registration. Incompleteness the target did not cause still blocks.
|
|
48
|
+
*/
|
|
49
|
+
export declare const planPackUninstallGraphReadiness: (graph: DesiredStateGraph, selectedPacks: ReadonlyArray<string>, scope: WorkspaceScope) => PackUninstallGraphReadiness;
|
|
50
|
+
//# sourceMappingURL=readiness.d.ts.map
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { planDesiredStateGraph } from "@agentxm/workspace-projection";
|
|
2
|
+
import { workspaceCanonicalRoot, workspaceLockfilePath, workspaceSettingsPath, } from "../../workspace-paths.js";
|
|
3
|
+
/** Recovery-conformance identity for Pack uninstall planning on an incomplete graph. */
|
|
4
|
+
export const PACK_UNINSTALL_GRAPH_BLOCKER_ID = "packs/uninstall/desired-state-graph-complete";
|
|
5
|
+
/** Executable Pack-uninstall blocker identities covered by recovery conformance. */
|
|
6
|
+
export const packUninstallRecoveryIdentifiers = [PACK_UNINSTALL_GRAPH_BLOCKER_ID];
|
|
7
|
+
const normalizedPack = (identity) => identity.startsWith("workspace:") ? identity.slice("workspace:".length) : identity;
|
|
8
|
+
/**
|
|
9
|
+
* Problems the lock pass emits for a Pack as a consequence of its unreadable
|
|
10
|
+
* manifest. They add no information the gate does not already have from the
|
|
11
|
+
* primary problem, so they never block on their own account.
|
|
12
|
+
*/
|
|
13
|
+
const isCompanionProblem = (problem) => problem.type === "pack-manifest-content-mismatch" ||
|
|
14
|
+
problem.type === "pack-resolution-unavailable";
|
|
15
|
+
/**
|
|
16
|
+
* Classify the selected Packs' own problems into retirements, or report that
|
|
17
|
+
* some selected Pack disagrees with the workspace in a way absence cannot
|
|
18
|
+
* explain. Readable-but-disagreeing state is drift, and stays blocked.
|
|
19
|
+
*/
|
|
20
|
+
const retirementsFor = (problems) => {
|
|
21
|
+
const problemsByPack = new Map();
|
|
22
|
+
for (const problem of problems) {
|
|
23
|
+
if (!("pack" in problem))
|
|
24
|
+
return undefined;
|
|
25
|
+
const pack = normalizedPack(problem.pack);
|
|
26
|
+
const existing = problemsByPack.get(pack);
|
|
27
|
+
if (existing === undefined)
|
|
28
|
+
problemsByPack.set(pack, [problem]);
|
|
29
|
+
else
|
|
30
|
+
existing.push(problem);
|
|
31
|
+
}
|
|
32
|
+
const retirements = [];
|
|
33
|
+
for (const [pack, packProblems] of problemsByPack) {
|
|
34
|
+
let unreadable;
|
|
35
|
+
for (const problem of packProblems) {
|
|
36
|
+
if (problem.type === "pack-manifest-unavailable") {
|
|
37
|
+
unreadable ??= { manifestPath: problem.path, reason: "missing" };
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (problem.type === "pack-manifest-invalid") {
|
|
41
|
+
unreadable ??= { manifestPath: problem.path, reason: "invalid" };
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (isCompanionProblem(problem))
|
|
45
|
+
continue;
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (unreadable === undefined)
|
|
49
|
+
return undefined;
|
|
50
|
+
retirements.push({ pack, ...unreadable });
|
|
51
|
+
}
|
|
52
|
+
return retirements;
|
|
53
|
+
};
|
|
54
|
+
const locationsFor = (problem, scope) => {
|
|
55
|
+
if ("path" in problem && problem.path !== undefined)
|
|
56
|
+
return [problem.path];
|
|
57
|
+
if ("pack" in problem)
|
|
58
|
+
return [workspaceSettingsPath(scope), workspaceLockfilePath(scope)];
|
|
59
|
+
return [workspaceSettingsPath(scope), `${workspaceCanonicalRoot(scope)}/*/packs/*/pack.json`];
|
|
60
|
+
};
|
|
61
|
+
const factFor = (problem, selectedPacks, scope) => {
|
|
62
|
+
const packs = "pack" in problem ? [normalizedPack(problem.pack)] : selectedPacks.map(normalizedPack);
|
|
63
|
+
const authoritativeLocations = locationsFor(problem, scope);
|
|
64
|
+
const member = "extensionType" in problem ? { type: problem.extensionType, name: problem.name } : undefined;
|
|
65
|
+
const subject = member === undefined ? `Pack ${packs.join(", ")}` : `${member.type} member ${member.name}`;
|
|
66
|
+
const problemDetail = "detail" in problem
|
|
67
|
+
? problem.detail
|
|
68
|
+
: "constraints" in problem
|
|
69
|
+
? `constraints ${problem.constraints.join(", ")}`
|
|
70
|
+
: "identities" in problem
|
|
71
|
+
? `identities ${problem.identities.join(", ")}`
|
|
72
|
+
: problem.type;
|
|
73
|
+
return {
|
|
74
|
+
problemType: problem.type,
|
|
75
|
+
packs,
|
|
76
|
+
...(member === undefined ? {} : { member }),
|
|
77
|
+
authoritativeLocations,
|
|
78
|
+
detail: `${subject}: ${problem.type} (${problemDetail}); authoritative location${authoritativeLocations.length === 1 ? "" : "s"}: ${authoritativeLocations.join(", ")}`,
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Build the Pack-uninstall readiness decision from the shared desired-state
|
|
83
|
+
* planner.
|
|
84
|
+
*
|
|
85
|
+
* The graph gate exists to stop a Pack transition computed from state AXM
|
|
86
|
+
* cannot read. A selected Pack whose own manifest is confirmed missing or
|
|
87
|
+
* undecodable is positive evidence about the removal target, not the absent
|
|
88
|
+
* evidence the gate guards against, so uninstall proceeds and removes only that
|
|
89
|
+
* Pack's registration. Incompleteness the target did not cause still blocks.
|
|
90
|
+
*/
|
|
91
|
+
export const planPackUninstallGraphReadiness = (graph, selectedPacks, scope) => {
|
|
92
|
+
const decision = planDesiredStateGraph(graph);
|
|
93
|
+
if (decision.readiness === "ready")
|
|
94
|
+
return { ...decision, retirements: [] };
|
|
95
|
+
const selected = new Set(selectedPacks.map(normalizedPack));
|
|
96
|
+
const foreign = [];
|
|
97
|
+
const own = [];
|
|
98
|
+
for (const problem of decision.problems) {
|
|
99
|
+
if ("pack" in problem && selected.has(normalizedPack(problem.pack)))
|
|
100
|
+
own.push(problem);
|
|
101
|
+
else
|
|
102
|
+
foreign.push(problem);
|
|
103
|
+
}
|
|
104
|
+
if (foreign.length === 0 && own.length > 0) {
|
|
105
|
+
const retirements = retirementsFor(own);
|
|
106
|
+
if (retirements !== undefined)
|
|
107
|
+
return { readiness: "ready", graph, retirements };
|
|
108
|
+
}
|
|
109
|
+
const facts = decision.problems.length === 0
|
|
110
|
+
? [
|
|
111
|
+
{
|
|
112
|
+
problemType: "unknown",
|
|
113
|
+
packs: selectedPacks.map(normalizedPack),
|
|
114
|
+
authoritativeLocations: [workspaceSettingsPath(scope), workspaceLockfilePath(scope)],
|
|
115
|
+
detail: `Pack ${selectedPacks.map(normalizedPack).join(", ")}: desired-state graph is incomplete; authoritative locations: ${workspaceSettingsPath(scope)}, ${workspaceLockfilePath(scope)}`,
|
|
116
|
+
},
|
|
117
|
+
]
|
|
118
|
+
: decision.problems.map((problem) => factFor(problem, selectedPacks, scope));
|
|
119
|
+
const foreignPacks = [
|
|
120
|
+
...new Set(foreign.flatMap((problem) => ("pack" in problem ? [normalizedPack(problem.pack)] : []))),
|
|
121
|
+
];
|
|
122
|
+
const remedy = foreignPacks.length === 0 ? "" : `; restore or uninstall ${foreignPacks.join(", ")} first`;
|
|
123
|
+
return {
|
|
124
|
+
readiness: "blocked",
|
|
125
|
+
id: PACK_UNINSTALL_GRAPH_BLOCKER_ID,
|
|
126
|
+
facts,
|
|
127
|
+
detail: `Cannot uninstall Pack graph because desired state is incomplete: ${facts.map((fact) => fact.detail).join("; ")}${remedy}`,
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=readiness.js.map
|
|
@@ -1,53 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Publisher
|
|
2
|
+
* Publisher-change plan shaping.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* forms, reinstall and re-resolution, and pack closures — classifies the
|
|
10
|
-
* transition from the structured accepted and proposed identities here, so
|
|
11
|
-
* the same rule holds everywhere the transition can occur.
|
|
12
|
-
*
|
|
13
|
-
* A first acceptance is not a change, and an integrity mismatch while
|
|
14
|
-
* reacquiring the same accepted identity remains a hard failure elsewhere.
|
|
4
|
+
* `@agentxm/extension-resolution` classifies whether a proposed Registry
|
|
5
|
+
* acceptance replaces an accepted publisher binding. This module turns that
|
|
6
|
+
* classification into plan vocabulary: the per-step warning, the
|
|
7
|
+
* interactive-only risk condition, and the blocking condition raised when an
|
|
8
|
+
* accepted resolution cannot be read at all.
|
|
15
9
|
*
|
|
16
10
|
* @experimental This API is unstable and may change without notice.
|
|
17
11
|
*/
|
|
18
12
|
import * as Effect from "effect/Effect";
|
|
19
13
|
import type * as FileSystem from "effect/FileSystem";
|
|
20
|
-
import * as Option from "effect/Option";
|
|
21
14
|
import type * as Path from "effect/Path";
|
|
22
|
-
import type
|
|
23
|
-
import type {
|
|
24
|
-
import type { Plan, PlanRiskCondition, RegistryBindingProposal } from "@agentxm/workspace-operations";
|
|
15
|
+
import { type PublisherBindingTransition } from "@agentxm/extension-resolution";
|
|
16
|
+
import type { Plan, PlanRiskCondition } from "@agentxm/workspace-operations";
|
|
25
17
|
import { WorkspaceMutations } from "@agentxm/workspace-state";
|
|
26
|
-
export interface PublisherBindingTransition {
|
|
27
|
-
readonly extensionType: ExtensionType;
|
|
28
|
-
/** The configured (local) name whose accepted resolution changes publisher. */
|
|
29
|
-
readonly target: string;
|
|
30
|
-
readonly owner: string;
|
|
31
|
-
readonly packageName: string;
|
|
32
|
-
/** The accepted publisher binding being replaced. */
|
|
33
|
-
readonly accepted: string;
|
|
34
|
-
/** The publisher binding the operation proposes to accept. */
|
|
35
|
-
readonly proposed: string;
|
|
36
|
-
}
|
|
37
18
|
export declare const PUBLISHER_CHANGE_CONDITION_ID = "publisher-ownership-change";
|
|
38
|
-
/** The structured proposal an extension ref makes, when it is a Registry ref. */
|
|
39
|
-
export declare const registryBindingProposal: (ref: ExtensionRef) => RegistryBindingProposal | undefined;
|
|
40
|
-
/**
|
|
41
|
-
* Classify one proposed acceptance against the accepted resolution for the
|
|
42
|
-
* same configured target. Only a Registry binding replacing a different
|
|
43
|
-
* Registry binding is a publisher change.
|
|
44
|
-
*/
|
|
45
|
-
export declare const classifyPublisherBindingTransition: (args: {
|
|
46
|
-
readonly accepted: Option.Option<ExtensionRef>;
|
|
47
|
-
readonly proposed: RegistryBindingProposal;
|
|
48
|
-
}) => Option.Option<PublisherBindingTransition>;
|
|
49
|
-
/** The warning a step carries when its acceptance changes publisher. */
|
|
50
|
-
export declare const publisherTransitionWarning: (transition: PublisherBindingTransition) => string;
|
|
51
19
|
/**
|
|
52
20
|
* The interactive-only condition a plan carries when any of its steps
|
|
53
21
|
* replaces an accepted publisher binding.
|