@agentxm/extension-lifecycle 0.28.12 → 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,579 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advancing what the workspace already accepted.
|
|
3
|
+
*
|
|
4
|
+
* Update is one use case with two shapes of request. A person can name one
|
|
5
|
+
* extension by its Registry identity, or ask the workspace to sweep every
|
|
6
|
+
* configured entry — optionally narrowed to one type, or to a selection of
|
|
7
|
+
* installed names. Both shapes answer the same question: within the intent
|
|
8
|
+
* the workspace recorded, is there a newer version its source is willing to
|
|
9
|
+
* offer, and may this workspace accept it?
|
|
10
|
+
*
|
|
11
|
+
* Everything that decides the answer lives here. A named update is classified
|
|
12
|
+
* against the desired graph first, so a refusal is reported before any
|
|
13
|
+
* Registry is contacted: an extension the workspace never asked for is not
|
|
14
|
+
* installed on its behalf, a Pack-owned member is not forked into direct
|
|
15
|
+
* intent, and a bundled or workspace-authored source is never replaced from
|
|
16
|
+
* the Registry. A sweep resolves each configured entry independently, so one
|
|
17
|
+
* unreachable source blocks one unit rather than the operation.
|
|
18
|
+
*
|
|
19
|
+
* `prepare` settles all of it and writes nothing: it produces either a frozen
|
|
20
|
+
* execution candidate or the settled reason there is nothing to execute.
|
|
21
|
+
* `previewOrApply` resolves that same candidate, so the preview a person
|
|
22
|
+
* reads and the apply that follows describe one decision.
|
|
23
|
+
*
|
|
24
|
+
* @experimental This API is unstable and may change without notice.
|
|
25
|
+
*/
|
|
26
|
+
import * as DateTime from "effect/DateTime";
|
|
27
|
+
import * as Effect from "effect/Effect";
|
|
28
|
+
import * as Option from "effect/Option";
|
|
29
|
+
import { HookManager, KnowledgeManager, McpServerManager, PackManager, RuleManager, SkillManager, SubagentManager, } from "@agentxm/extension-materialization";
|
|
30
|
+
import { parseSourceQualifiedRegistrySourcePatternParts, decodeExtensionNameSync, } from "@agentxm/extension-model/unstable/extensions";
|
|
31
|
+
import { decodeVersionRangeSync, versionSatisfiesRange, } from "@agentxm/extension-model/unstable/version-constraints";
|
|
32
|
+
import { ReleaseAgePosture, makeConfiguredReleaseAgeEvaluation, normalizeReleaseAgeRecords, resolveTargetedUpdateContext, } from "@agentxm/extension-resolution";
|
|
33
|
+
import { SourceHostProviders, WorkspaceCatalog, resolveSource, } from "@agentxm/extension-sources";
|
|
34
|
+
import { makeOperationResolution, operationPresentation, prepareExecutionCandidate, resolveExecutionCandidate, } from "@agentxm/workspace-operations";
|
|
35
|
+
import { WorkspaceMutations, WorkspaceRecords, acceptedResolutionRef, usableAcceptedCanonical, } from "@agentxm/workspace-state";
|
|
36
|
+
import { ExtensionLifecycleFailed } from "../errors.js";
|
|
37
|
+
import { buildPackMemberInstallStep } from "../packs/member-install-step.js";
|
|
38
|
+
import { withPublisherTrust } from "../publisher-binding.js";
|
|
39
|
+
import { planHookInstall } from "../hooks/install/plan.js";
|
|
40
|
+
import { planKnowledgeInstall } from "../knowledge/install/plan.js";
|
|
41
|
+
import { planMcpServerInstall } from "../mcps/install/plan.js";
|
|
42
|
+
import { planPackInstall } from "../packs/install/plan.js";
|
|
43
|
+
import { planRuleInstall } from "../rules/install/plan.js";
|
|
44
|
+
import { planSkillInstall } from "../skills/install/plan.js";
|
|
45
|
+
import { planSubagentInstall } from "../subagents/install/plan.js";
|
|
46
|
+
import { blockerClass, blockerDetail, staleOutputContext } from "./blockers.js";
|
|
47
|
+
import { buildWorkspaceUpdatePlan } from "./configured.js";
|
|
48
|
+
import { resolveConfiguredUpdateSelection } from "./selector.js";
|
|
49
|
+
import { resolveRootUpdateIntent } from "./root-request.js";
|
|
50
|
+
import { wrapTargetedUpdatePlan } from "./targeted-plan.js";
|
|
51
|
+
// -----------------------------------------------------------------------------
|
|
52
|
+
// Release-age evidence
|
|
53
|
+
// -----------------------------------------------------------------------------
|
|
54
|
+
const updatePresentation = (type) => operationPresentation({ imperative: "update", past: "Updated", gerund: "Updating" }, type);
|
|
55
|
+
const releaseAgeRecord = (args) => ({
|
|
56
|
+
reason: "minimum-release-age",
|
|
57
|
+
target: args.intent.target,
|
|
58
|
+
dependencyPath: [args.intent.target],
|
|
59
|
+
...Option.match(args.intent.versionRange, {
|
|
60
|
+
onNone: () => ({}),
|
|
61
|
+
onSome: (requestedRange) => ({ requestedRange }),
|
|
62
|
+
}),
|
|
63
|
+
...(args.currentVersion === undefined ? {} : { currentVersion: args.currentVersion }),
|
|
64
|
+
...(args.selectedVersion === undefined ? {} : { selectedVersion: args.selectedVersion }),
|
|
65
|
+
candidateVersion: args.evidence.version,
|
|
66
|
+
publishedAt: args.evidence.publishedAt,
|
|
67
|
+
eligibleAt: args.evidence.eligibleAt,
|
|
68
|
+
minimumReleaseAgeSeconds: args.evidence.minimumReleaseAgeSeconds,
|
|
69
|
+
});
|
|
70
|
+
// -----------------------------------------------------------------------------
|
|
71
|
+
// Desired-state facts a targeted update is decided against
|
|
72
|
+
// -----------------------------------------------------------------------------
|
|
73
|
+
const normalizedPackIdentity = (identity) => identity.startsWith("workspace:") ? identity.slice("workspace:".length) : identity;
|
|
74
|
+
const desiredNodeForIntent = (graph, intent) => graph.nodes.find((node) => {
|
|
75
|
+
if (node.type !== intent.type)
|
|
76
|
+
return false;
|
|
77
|
+
if (normalizedPackIdentity(node.identity) === intent.target)
|
|
78
|
+
return true;
|
|
79
|
+
if (node.type !== "mcp-server" || node.source === undefined)
|
|
80
|
+
return false;
|
|
81
|
+
const parsed = parseSourceQualifiedRegistrySourcePatternParts(node.source);
|
|
82
|
+
return (parsed?.name !== undefined &&
|
|
83
|
+
`${parsed.owner}/${parsed.type}/${parsed.name}` === intent.target);
|
|
84
|
+
});
|
|
85
|
+
/**
|
|
86
|
+
* The version a held release would preserve: the accepted one, when it is
|
|
87
|
+
* still usable, still the same package, and still inside the requested range.
|
|
88
|
+
* A Pack preserves only when its whole member closure is usable too.
|
|
89
|
+
*/
|
|
90
|
+
const preservableRegistryVersion = Effect.fn("UpdateExtensions.preservableVersion")(function* (intent) {
|
|
91
|
+
const workspace = yield* WorkspaceMutations;
|
|
92
|
+
const graph = yield* workspace.getDesiredStateGraph();
|
|
93
|
+
if (!graph.complete)
|
|
94
|
+
return Option.none();
|
|
95
|
+
const desired = desiredNodeForIntent(graph, intent);
|
|
96
|
+
if (desired === undefined)
|
|
97
|
+
return Option.none();
|
|
98
|
+
const canonical = yield* usableAcceptedCanonical({
|
|
99
|
+
workspace,
|
|
100
|
+
type: intent.type,
|
|
101
|
+
name: desired.name,
|
|
102
|
+
});
|
|
103
|
+
if (Option.isNone(canonical))
|
|
104
|
+
return Option.none();
|
|
105
|
+
const ref = canonical.value.ref;
|
|
106
|
+
if (ref.refType !== "registry" || ref.owner !== intent.owner || ref.name !== intent.name) {
|
|
107
|
+
return Option.none();
|
|
108
|
+
}
|
|
109
|
+
if (Option.isSome(intent.versionRange) &&
|
|
110
|
+
!versionSatisfiesRange(ref.version, decodeVersionRangeSync(intent.versionRange.value))) {
|
|
111
|
+
return Option.none();
|
|
112
|
+
}
|
|
113
|
+
if (intent.type === "pack") {
|
|
114
|
+
const graphNodes = graph.nodes.filter((node) => (node.type === "pack" && node.name === intent.name) ||
|
|
115
|
+
node.origins.some((origin) => origin.type === "pack" && normalizedPackIdentity(origin.pack) === intent.target));
|
|
116
|
+
const usable = yield* Effect.forEach(graphNodes, (node) => usableAcceptedCanonical({ workspace, type: node.type, name: node.name }).pipe(Effect.map(Option.isSome)));
|
|
117
|
+
if (usable.some((value) => !value))
|
|
118
|
+
return Option.none();
|
|
119
|
+
}
|
|
120
|
+
return Option.some(ref.version);
|
|
121
|
+
});
|
|
122
|
+
/** The accepted version a resolution must not fall below, when there is one. */
|
|
123
|
+
const acceptedRegistryFloor = Effect.fn("UpdateExtensions.acceptedFloor")(function* (intent) {
|
|
124
|
+
const workspace = yield* WorkspaceMutations;
|
|
125
|
+
const graph = yield* workspace.getDesiredStateGraph();
|
|
126
|
+
const desired = desiredNodeForIntent(graph, intent);
|
|
127
|
+
if (desired === undefined) {
|
|
128
|
+
return Option.none();
|
|
129
|
+
}
|
|
130
|
+
const accepted = yield* acceptedResolutionRef({
|
|
131
|
+
workspace,
|
|
132
|
+
type: intent.type,
|
|
133
|
+
name: desired.name,
|
|
134
|
+
});
|
|
135
|
+
return Option.flatMap(accepted, (ref) => ref.refType === "registry" && ref.owner === intent.owner && ref.name === intent.name
|
|
136
|
+
? Option.some({ version: ref.version, publisherBindingId: ref.publisherBindingId })
|
|
137
|
+
: Option.none());
|
|
138
|
+
});
|
|
139
|
+
// -----------------------------------------------------------------------------
|
|
140
|
+
// Per-type planning from one resolved ref
|
|
141
|
+
// -----------------------------------------------------------------------------
|
|
142
|
+
/**
|
|
143
|
+
* Plan the advance for one resolved Registry ref through the same per-type
|
|
144
|
+
* planner the install routes use: advancing an accepted resolution is
|
|
145
|
+
* installing the version its source now offers.
|
|
146
|
+
*/
|
|
147
|
+
const planResolvedTarget = Effect.fn("UpdateExtensions.planResolvedTarget")(function* (args) {
|
|
148
|
+
const { intent, ref, versionRange } = args;
|
|
149
|
+
const mismatch = () => Effect.fail(new ExtensionLifecycleFailed({
|
|
150
|
+
category: "internal",
|
|
151
|
+
detail: `Registry resolved ${intent.target} as ${ref.type}, expected ${intent.type}`,
|
|
152
|
+
}));
|
|
153
|
+
switch (intent.type) {
|
|
154
|
+
case "skill":
|
|
155
|
+
return ref.type !== "skill"
|
|
156
|
+
? yield* mismatch()
|
|
157
|
+
: yield* planSkillInstall({ skillsToInstall: [{ ref, versionRange }] });
|
|
158
|
+
case "subagent":
|
|
159
|
+
return ref.type !== "subagent"
|
|
160
|
+
? yield* mismatch()
|
|
161
|
+
: yield* planSubagentInstall({ subagentsToInstall: [{ ref, versionRange }] });
|
|
162
|
+
case "rule":
|
|
163
|
+
return ref.type !== "rule"
|
|
164
|
+
? yield* mismatch()
|
|
165
|
+
: yield* planRuleInstall({ refs: [{ ref, versionRange }] });
|
|
166
|
+
case "hook":
|
|
167
|
+
return ref.type !== "hook"
|
|
168
|
+
? yield* mismatch()
|
|
169
|
+
: yield* planHookInstall({ refs: [{ ref, versionRange }] });
|
|
170
|
+
case "knowledge":
|
|
171
|
+
return ref.type !== "knowledge"
|
|
172
|
+
? yield* mismatch()
|
|
173
|
+
: yield* planKnowledgeInstall({ refs: [{ ref, versionRange }] });
|
|
174
|
+
case "mcp-server": {
|
|
175
|
+
if (ref.type !== "mcp-server")
|
|
176
|
+
return yield* mismatch();
|
|
177
|
+
const workspace = yield* WorkspaceMutations;
|
|
178
|
+
const graph = yield* workspace.getDesiredStateGraph();
|
|
179
|
+
const desired = desiredNodeForIntent(graph, intent);
|
|
180
|
+
return yield* planMcpServerInstall({
|
|
181
|
+
ref,
|
|
182
|
+
localName: decodeExtensionNameSync(desired?.name ?? ref.server.name),
|
|
183
|
+
versionRange: Option.map(versionRange, (range) => range),
|
|
184
|
+
force: false,
|
|
185
|
+
nonInteractive: args.nonInteractive,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
case "pack":
|
|
189
|
+
return ref.type !== "pack"
|
|
190
|
+
? yield* mismatch()
|
|
191
|
+
: yield* planPackInstall({
|
|
192
|
+
packToInstall: ref,
|
|
193
|
+
versionRange,
|
|
194
|
+
unattended: true,
|
|
195
|
+
nonInteractive: args.nonInteractive,
|
|
196
|
+
releaseAgeEvaluation: args.releaseAgeEvaluation,
|
|
197
|
+
releaseAgeHoldbackBehavior: "preserve-or-block",
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
// -----------------------------------------------------------------------------
|
|
202
|
+
// Targeted preparation
|
|
203
|
+
// -----------------------------------------------------------------------------
|
|
204
|
+
const blockedCandidate = (context) => ({
|
|
205
|
+
outcome: "blocked",
|
|
206
|
+
name: `Update ${context.target.fqn}`,
|
|
207
|
+
detail: blockerDetail(context),
|
|
208
|
+
subject: context.target.fqn,
|
|
209
|
+
subjectType: context.target.type,
|
|
210
|
+
blockingClass: blockerClass(context.blocker),
|
|
211
|
+
...(context.blocker === undefined ? {} : { reference: context.blocker }),
|
|
212
|
+
targetedContext: context,
|
|
213
|
+
});
|
|
214
|
+
/**
|
|
215
|
+
* A Pack is classified as a closure rather than a member, so the
|
|
216
|
+
* member-oriented blockers never see it. Desired state still gates it:
|
|
217
|
+
* updating a Pack the workspace never asked for would acquire the Pack and
|
|
218
|
+
* its whole member closure.
|
|
219
|
+
*/
|
|
220
|
+
const blockedUndesiredPack = (intent) => ({
|
|
221
|
+
outcome: "blocked",
|
|
222
|
+
name: `Update ${intent.target}`,
|
|
223
|
+
detail: `${intent.target} is not desired by this workspace`,
|
|
224
|
+
subject: intent.target,
|
|
225
|
+
subjectType: intent.type,
|
|
226
|
+
blockingClass: "precondition-unmet",
|
|
227
|
+
reference: "not-desired",
|
|
228
|
+
});
|
|
229
|
+
/** The outcome a held release settles into: preserve, or block with evidence. */
|
|
230
|
+
const heldCandidate = Effect.fn("UpdateExtensions.heldCandidate")(function* (args) {
|
|
231
|
+
const currentVersion = yield* preservableRegistryVersion(args.intent);
|
|
232
|
+
const record = releaseAgeRecord({
|
|
233
|
+
intent: args.intent,
|
|
234
|
+
evidence: args.evidence,
|
|
235
|
+
...(Option.isNone(currentVersion)
|
|
236
|
+
? {}
|
|
237
|
+
: { currentVersion: currentVersion.value, selectedVersion: currentVersion.value }),
|
|
238
|
+
});
|
|
239
|
+
const releaseAge = {
|
|
240
|
+
evaluatedAt: args.evaluatedAt,
|
|
241
|
+
holdbacks: [record],
|
|
242
|
+
bypasses: [],
|
|
243
|
+
};
|
|
244
|
+
if (Option.isSome(currentVersion)) {
|
|
245
|
+
return {
|
|
246
|
+
outcome: "preserved",
|
|
247
|
+
name: `Update ${args.intent.target}`,
|
|
248
|
+
description: `Preserve ${args.intent.target} until its selected release is eligible`,
|
|
249
|
+
subjectType: args.intent.type,
|
|
250
|
+
releaseAge,
|
|
251
|
+
...(args.context === undefined ? {} : { targetedContext: args.context }),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
outcome: "blocked",
|
|
256
|
+
name: `Update ${args.intent.target}`,
|
|
257
|
+
detail: `${args.intent.target}'s selected release is held by the minimum release age until ${args.evidence.eligibleAt}`,
|
|
258
|
+
subject: args.intent.target,
|
|
259
|
+
subjectType: args.intent.type,
|
|
260
|
+
blockingClass: "policy-excluded",
|
|
261
|
+
reference: "release-age-held",
|
|
262
|
+
releaseAge,
|
|
263
|
+
...(args.context === undefined ? {} : { targetedContext: args.context }),
|
|
264
|
+
};
|
|
265
|
+
});
|
|
266
|
+
/** Settle a request that names one extension by its Registry identity. */
|
|
267
|
+
const prepareTargeted = Effect.fn("UpdateExtensions.prepareTargeted")(function* (request) {
|
|
268
|
+
const intent = yield* resolveRootUpdateIntent(request.source);
|
|
269
|
+
let targetedContext;
|
|
270
|
+
if (intent.type === "pack") {
|
|
271
|
+
const workspace = yield* WorkspaceMutations;
|
|
272
|
+
const graph = yield* workspace.getDesiredStateGraph();
|
|
273
|
+
if (desiredNodeForIntent(graph, intent) === undefined) {
|
|
274
|
+
return blockedUndesiredPack(intent);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
targetedContext = yield* resolveTargetedUpdateContext({
|
|
279
|
+
target: { type: intent.type, name: intent.name, fqn: intent.target },
|
|
280
|
+
...(Option.isNone(intent.versionRange) ? {} : { explicitRange: intent.versionRange.value }),
|
|
281
|
+
});
|
|
282
|
+
if (targetedContext.public.blocker !== undefined) {
|
|
283
|
+
return blockedCandidate(targetedContext.public);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const releaseAgeEvaluation = yield* makeConfiguredReleaseAgeEvaluation();
|
|
287
|
+
const evaluatedAtText = DateTime.formatIso(releaseAgeEvaluation.evaluatedAt);
|
|
288
|
+
const source = yield* resolveSource(intent.source);
|
|
289
|
+
if (source.type !== "registry") {
|
|
290
|
+
return yield* new ExtensionLifecycleFailed({
|
|
291
|
+
category: "usage",
|
|
292
|
+
detail: "Root update requires a Registry FQN",
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
const providers = yield* SourceHostProviders;
|
|
296
|
+
const accepted = yield* acceptedRegistryFloor(intent);
|
|
297
|
+
const selected = yield* providers.resolveNamedRegistry(source, {
|
|
298
|
+
name: intent.name,
|
|
299
|
+
type: intent.type,
|
|
300
|
+
owner: intent.owner,
|
|
301
|
+
versionRange: targetedContext?.public.effectiveConstraint === undefined
|
|
302
|
+
? intent.versionRange
|
|
303
|
+
: Option.some(decodeVersionRangeSync(targetedContext.public.effectiveConstraint)),
|
|
304
|
+
releaseAgeEvaluation,
|
|
305
|
+
...(Option.isSome(accepted) ? { accepted: accepted.value } : {}),
|
|
306
|
+
});
|
|
307
|
+
if (selected.kind === "not_found") {
|
|
308
|
+
return yield* new ExtensionLifecycleFailed({
|
|
309
|
+
category: "not_found",
|
|
310
|
+
detail: `Registry extension "${selected.target}" was not found`,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
if (selected.kind === "version_unsatisfied") {
|
|
314
|
+
return yield* new ExtensionLifecycleFailed({
|
|
315
|
+
category: "conflict",
|
|
316
|
+
title: "No compatible version",
|
|
317
|
+
detail: `No visible version of "${selected.target}" satisfies ${selected.requestedRange}`,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
if (selected.kind === "policy_held") {
|
|
321
|
+
return yield* heldCandidate({
|
|
322
|
+
intent,
|
|
323
|
+
evidence: selected.candidate,
|
|
324
|
+
evaluatedAt: evaluatedAtText,
|
|
325
|
+
...(targetedContext === undefined ? {} : { context: targetedContext.public }),
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
// A Pack-owned member advances inside its Pack's graph, so its step is the
|
|
329
|
+
// member transition rather than a direct install: nothing writes a direct
|
|
330
|
+
// declaration the workspace did not ask for.
|
|
331
|
+
const plan = targetedContext?.public.authority === "pack-aware"
|
|
332
|
+
? yield* wrapTargetedUpdatePlan({
|
|
333
|
+
plan: yield* withPublisherTrust({
|
|
334
|
+
_tag: "Plan",
|
|
335
|
+
name: `Update ${intent.target}`,
|
|
336
|
+
description: Option.some(`Update pack-derived member ${intent.target}`),
|
|
337
|
+
jobs: [
|
|
338
|
+
{
|
|
339
|
+
concurrency: 1,
|
|
340
|
+
steps: [
|
|
341
|
+
yield* buildPackMemberInstallStep({
|
|
342
|
+
ref: yield* requirePackMemberRef(intent, selected.ref),
|
|
343
|
+
graphComplete: true,
|
|
344
|
+
nonInteractive: request.nonInteractive,
|
|
345
|
+
}),
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
}),
|
|
350
|
+
context: targetedContext,
|
|
351
|
+
})
|
|
352
|
+
: yield* planTargetedDirect({
|
|
353
|
+
intent,
|
|
354
|
+
ref: selected.ref,
|
|
355
|
+
nonInteractive: request.nonInteractive,
|
|
356
|
+
releaseAgeEvaluation,
|
|
357
|
+
...(targetedContext === undefined ? {} : { context: targetedContext }),
|
|
358
|
+
});
|
|
359
|
+
const holdbacks = selected.kind === "exempted" || selected.newerHeld === undefined
|
|
360
|
+
? []
|
|
361
|
+
: [
|
|
362
|
+
releaseAgeRecord({
|
|
363
|
+
intent,
|
|
364
|
+
evidence: selected.newerHeld,
|
|
365
|
+
selectedVersion: selected.ref.version,
|
|
366
|
+
...(Option.isSome(accepted) && accepted.value.version === selected.ref.version
|
|
367
|
+
? { currentVersion: accepted.value.version }
|
|
368
|
+
: {}),
|
|
369
|
+
}),
|
|
370
|
+
];
|
|
371
|
+
const bypasses = selected.kind === "selected"
|
|
372
|
+
? []
|
|
373
|
+
: [
|
|
374
|
+
{
|
|
375
|
+
...releaseAgeRecord({
|
|
376
|
+
intent,
|
|
377
|
+
evidence: selected.bypassed,
|
|
378
|
+
selectedVersion: selected.ref.version,
|
|
379
|
+
}),
|
|
380
|
+
...selected.exemption,
|
|
381
|
+
},
|
|
382
|
+
];
|
|
383
|
+
return {
|
|
384
|
+
outcome: "planned",
|
|
385
|
+
subjectType: intent.type,
|
|
386
|
+
execution: yield* prepareExecutionCandidate(plan),
|
|
387
|
+
releaseAge: { evaluatedAt: evaluatedAtText, holdbacks, bypasses },
|
|
388
|
+
...(targetedContext === undefined
|
|
389
|
+
? {}
|
|
390
|
+
: { targetedContext: targetedContext.public, fingerprintedContext: targetedContext }),
|
|
391
|
+
};
|
|
392
|
+
});
|
|
393
|
+
/** A Pack member step accepts every installable ref except a nested Pack. */
|
|
394
|
+
const requirePackMemberRef = (intent, ref) => ref.type === "pack"
|
|
395
|
+
? Effect.fail(new ExtensionLifecycleFailed({
|
|
396
|
+
category: "internal",
|
|
397
|
+
detail: `Registry resolved ${intent.target} as an unexpected pack`,
|
|
398
|
+
}))
|
|
399
|
+
: ref.type !== intent.type
|
|
400
|
+
? Effect.fail(new ExtensionLifecycleFailed({
|
|
401
|
+
category: "internal",
|
|
402
|
+
detail: `Registry resolved ${intent.target} as ${ref.type}, expected ${intent.type}`,
|
|
403
|
+
}))
|
|
404
|
+
: Effect.succeed(ref);
|
|
405
|
+
/**
|
|
406
|
+
* Plan a direct advance. The durable range a direct declaration keeps is the
|
|
407
|
+
* one the request named, or the one the declaration already recorded — a
|
|
408
|
+
* targeted update never widens the intent the workspace expressed.
|
|
409
|
+
*/
|
|
410
|
+
const planTargetedDirect = Effect.fn("UpdateExtensions.planTargetedDirect")(function* (args) {
|
|
411
|
+
const { intent, context } = args;
|
|
412
|
+
const durableRange = intent.type === "pack"
|
|
413
|
+
? intent.versionRange
|
|
414
|
+
: Option.fromUndefinedOr(Option.getOrUndefined(intent.versionRange) ?? context?.public.direct?.constraint).pipe(Option.map(decodeVersionRangeSync));
|
|
415
|
+
const planned = yield* planResolvedTarget({
|
|
416
|
+
intent,
|
|
417
|
+
ref: args.ref,
|
|
418
|
+
versionRange: durableRange,
|
|
419
|
+
nonInteractive: args.nonInteractive,
|
|
420
|
+
releaseAgeEvaluation: args.releaseAgeEvaluation,
|
|
421
|
+
});
|
|
422
|
+
if (context === undefined) {
|
|
423
|
+
return {
|
|
424
|
+
...planned,
|
|
425
|
+
presentation: updatePresentation(intent.type),
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
return yield* wrapTargetedUpdatePlan({
|
|
429
|
+
plan: yield* withPublisherTrust(planned),
|
|
430
|
+
context,
|
|
431
|
+
...(Option.isNone(intent.versionRange) ? {} : { explicitRange: intent.versionRange.value }),
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
// -----------------------------------------------------------------------------
|
|
435
|
+
// Configured preparation
|
|
436
|
+
// -----------------------------------------------------------------------------
|
|
437
|
+
/**
|
|
438
|
+
* Which configured-agent outcomes the candidate projects: every selected name
|
|
439
|
+
* whose unit can actually converge. A unit that is not applicable, or that
|
|
440
|
+
* failed to plan, is excluded — projecting an outcome for it would promise an
|
|
441
|
+
* agent state nothing is going to establish.
|
|
442
|
+
*/
|
|
443
|
+
const configuredAgentOperations = (plan, type, names) => Option.match(type, {
|
|
444
|
+
onNone: () => [],
|
|
445
|
+
onSome: (extensionType) => {
|
|
446
|
+
const nonConverging = new Set(plan.jobs.flatMap((job) => job.steps.flatMap((step) => step.key?.startsWith("not-applicable:") === true ||
|
|
447
|
+
step.key?.endsWith(":planning-error") === true
|
|
448
|
+
? [step.label]
|
|
449
|
+
: [])));
|
|
450
|
+
return [
|
|
451
|
+
...new Set(names ??
|
|
452
|
+
plan.jobs.flatMap((job) => job.steps.map((step) => step.label.replace(/^(?:Skip|Update)\s+/u, "")))),
|
|
453
|
+
]
|
|
454
|
+
.filter((name) => !nonConverging.has(name))
|
|
455
|
+
.map((name) => ({ extensionType, name, plannedState: "enabled" }));
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
/** Settle a request that sweeps the configured entries. */
|
|
459
|
+
const prepareConfigured = Effect.fn("UpdateExtensions.prepareConfigured")(function* (request) {
|
|
460
|
+
const nothingToAdvance = (message) => ({
|
|
461
|
+
outcome: "nothing-configured",
|
|
462
|
+
message,
|
|
463
|
+
subjectType: Option.getOrElse(request.type, () => "mixed"),
|
|
464
|
+
planDescription: request.planDescription,
|
|
465
|
+
});
|
|
466
|
+
const selection = request.selector === undefined
|
|
467
|
+
? { _tag: "All" }
|
|
468
|
+
: yield* resolveConfiguredUpdateSelection(request.selector);
|
|
469
|
+
if (selection._tag === "NoMatch")
|
|
470
|
+
return nothingToAdvance(selection.message);
|
|
471
|
+
const names = selection._tag === "Names" ? selection.names : undefined;
|
|
472
|
+
const result = yield* buildWorkspaceUpdatePlan({
|
|
473
|
+
type: request.type,
|
|
474
|
+
planName: request.planName,
|
|
475
|
+
planDescription: request.planDescription,
|
|
476
|
+
nonInteractive: request.nonInteractive,
|
|
477
|
+
...(names === undefined ? {} : { names }),
|
|
478
|
+
});
|
|
479
|
+
if (result._tag === "NoConfiguredExtensions") {
|
|
480
|
+
return nothingToAdvance(result.message);
|
|
481
|
+
}
|
|
482
|
+
// Every Registry acceptance the sweep proposed is classified against the
|
|
483
|
+
// accepted resolution, so a replaced publisher binding carries the same
|
|
484
|
+
// interactive-only condition here as on the install routes.
|
|
485
|
+
const plan = yield* withPublisherTrust(result.plan);
|
|
486
|
+
return {
|
|
487
|
+
outcome: "planned",
|
|
488
|
+
subjectType: Option.getOrElse(request.type, () => "mixed"),
|
|
489
|
+
execution: yield* prepareExecutionCandidate(plan, {
|
|
490
|
+
configuredAgentOperations: configuredAgentOperations(plan, request.type, names),
|
|
491
|
+
}),
|
|
492
|
+
...(names === undefined ? {} : { selectedNames: names }),
|
|
493
|
+
};
|
|
494
|
+
});
|
|
495
|
+
// -----------------------------------------------------------------------------
|
|
496
|
+
// prepare
|
|
497
|
+
// -----------------------------------------------------------------------------
|
|
498
|
+
/**
|
|
499
|
+
* Settle an update: read the request, decide what may advance, and freeze
|
|
500
|
+
* either the execution candidate or the reason there is nothing to execute.
|
|
501
|
+
*/
|
|
502
|
+
export const prepareUpdate = Effect.fn("UpdateExtensions.prepare")(function* (request) {
|
|
503
|
+
return request.kind === "targeted"
|
|
504
|
+
? yield* prepareTargeted(request)
|
|
505
|
+
: yield* prepareConfigured(request);
|
|
506
|
+
});
|
|
507
|
+
// -----------------------------------------------------------------------------
|
|
508
|
+
// previewOrApply
|
|
509
|
+
// -----------------------------------------------------------------------------
|
|
510
|
+
/** A settled refusal or preservation, said as one operation outcome. */
|
|
511
|
+
const settledResolution = (candidate, execution) => {
|
|
512
|
+
const mode = execution.request.mode;
|
|
513
|
+
switch (candidate.outcome) {
|
|
514
|
+
case "blocked":
|
|
515
|
+
return makeOperationResolution({
|
|
516
|
+
name: candidate.name,
|
|
517
|
+
description: Option.some(candidate.detail),
|
|
518
|
+
mode,
|
|
519
|
+
atomicity: { declared: "closure-atomic", applied: "closure-atomic" },
|
|
520
|
+
units: [],
|
|
521
|
+
presentation: updatePresentation(candidate.subjectType),
|
|
522
|
+
...(candidate.releaseAge === undefined ? {} : { releaseAge: candidate.releaseAge }),
|
|
523
|
+
blocking: {
|
|
524
|
+
class: candidate.blockingClass,
|
|
525
|
+
subject: candidate.subject,
|
|
526
|
+
phase: "planning",
|
|
527
|
+
detail: candidate.detail,
|
|
528
|
+
causeCode: "conflict",
|
|
529
|
+
...(candidate.reference === undefined ? {} : { reference: candidate.reference }),
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
case "preserved":
|
|
533
|
+
return makeOperationResolution({
|
|
534
|
+
name: candidate.name,
|
|
535
|
+
description: Option.some(candidate.description),
|
|
536
|
+
mode,
|
|
537
|
+
atomicity: { declared: "closure-atomic", applied: "closure-atomic" },
|
|
538
|
+
units: [],
|
|
539
|
+
presentation: updatePresentation(candidate.subjectType),
|
|
540
|
+
releaseAge: candidate.releaseAge,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
/**
|
|
545
|
+
* Preview or apply a settled update, resolving to one operation outcome.
|
|
546
|
+
*
|
|
547
|
+
* A sweep that found nothing configured has no operation to resolve: the
|
|
548
|
+
* application reports it as the no-op it is, so this takes only the
|
|
549
|
+
* candidates that do describe an operation.
|
|
550
|
+
*/
|
|
551
|
+
export const previewOrApplyUpdate = (candidate, execution) => candidate.outcome === "planned"
|
|
552
|
+
? resolveExecutionCandidate(candidate.execution, execution).pipe(Effect.map((resolution) => candidate.releaseAge === undefined
|
|
553
|
+
? resolution
|
|
554
|
+
: {
|
|
555
|
+
...resolution,
|
|
556
|
+
releaseAge: {
|
|
557
|
+
evaluatedAt: candidate.releaseAge.evaluatedAt,
|
|
558
|
+
holdbacks: normalizeReleaseAgeRecords([
|
|
559
|
+
...candidate.releaseAge.holdbacks,
|
|
560
|
+
...(resolution.releaseAge?.holdbacks ?? []),
|
|
561
|
+
]),
|
|
562
|
+
bypasses: normalizeReleaseAgeRecords([
|
|
563
|
+
...candidate.releaseAge.bypasses,
|
|
564
|
+
...(resolution.releaseAge?.bypasses ?? []),
|
|
565
|
+
]),
|
|
566
|
+
},
|
|
567
|
+
}))
|
|
568
|
+
: Effect.succeed(settledResolution(candidate, execution));
|
|
569
|
+
/**
|
|
570
|
+
* The ownership context an outcome reports: a stale-candidate outcome says
|
|
571
|
+
* nothing moved, whatever the classification said before the transition.
|
|
572
|
+
*/
|
|
573
|
+
export const contextForResolution = (context, resolution) => resolution.blocking?.class === "stale-candidate" ? staleOutputContext(context) : context;
|
|
574
|
+
/** The update use case: settle a request, then preview or apply it. */
|
|
575
|
+
export const UpdateExtensions = {
|
|
576
|
+
prepare: prepareUpdate,
|
|
577
|
+
previewOrApply: previewOrApplyUpdate,
|
|
578
|
+
};
|
|
579
|
+
//# sourceMappingURL=update-extensions.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable workspace-relative paths a plan and its result name.
|
|
3
|
+
*
|
|
4
|
+
* A removal reports what it touched — settings, the lockfile, the canonical
|
|
5
|
+
* package — as paths a person can open. They are derived from the workspace
|
|
6
|
+
* layout rather than from where the workspace happens to sit on disk, so the
|
|
7
|
+
* same operation reads identically from any checkout.
|
|
8
|
+
*
|
|
9
|
+
* @experimental This API is unstable and may change without notice.
|
|
10
|
+
*/
|
|
11
|
+
import type * as Path from "effect/Path";
|
|
12
|
+
import type { ExtensionType } from "@agentxm/extension-model/unstable/extensions";
|
|
13
|
+
import type { WorkspaceScope } from "@agentxm/extension-model/unstable/workspace-scope";
|
|
14
|
+
import type { WorkspaceMutationsService } from "@agentxm/workspace-state";
|
|
15
|
+
/** Where the workspace's settings file sits, relative to the workspace root. */
|
|
16
|
+
export declare const workspaceSettingsPath: (scope: WorkspaceScope) => string;
|
|
17
|
+
/** Where the workspace's lockfile sits, relative to the workspace root. */
|
|
18
|
+
export declare const workspaceLockfilePath: (scope: WorkspaceScope) => string;
|
|
19
|
+
/** The root every acquired package sits under, relative to the workspace root. */
|
|
20
|
+
export declare const workspaceCanonicalRoot: (scope: WorkspaceScope) => string;
|
|
21
|
+
/** One acquired package's path, relative to the workspace root. */
|
|
22
|
+
export declare const workspaceCanonicalPath: (scope: WorkspaceScope, relativePath: string) => string;
|
|
23
|
+
/** Where one desired-graph node's package sits, authored or acquired. */
|
|
24
|
+
export declare const workspaceCanonicalNodePath: (path: Path.Path, ws: WorkspaceMutationsService, node: {
|
|
25
|
+
readonly type: ExtensionType;
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly identity: string;
|
|
28
|
+
}) => string;
|
|
29
|
+
/** Where a workspace-authored package of this type and name sits. */
|
|
30
|
+
export declare const workspaceAuthoredPath: (path: Path.Path, ws: WorkspaceMutationsService, type: ExtensionType, name: string) => string;
|
|
31
|
+
//# sourceMappingURL=workspace-paths.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable workspace-relative paths a plan and its result name.
|
|
3
|
+
*
|
|
4
|
+
* A removal reports what it touched — settings, the lockfile, the canonical
|
|
5
|
+
* package — as paths a person can open. They are derived from the workspace
|
|
6
|
+
* layout rather than from where the workspace happens to sit on disk, so the
|
|
7
|
+
* same operation reads identically from any checkout.
|
|
8
|
+
*
|
|
9
|
+
* @experimental This API is unstable and may change without notice.
|
|
10
|
+
*/
|
|
11
|
+
/** Where the workspace's settings file sits, relative to the workspace root. */
|
|
12
|
+
export const workspaceSettingsPath = (scope) => scope === "project" ? "axm.json" : ".axm/workspace/axm.json";
|
|
13
|
+
/** Where the workspace's lockfile sits, relative to the workspace root. */
|
|
14
|
+
export const workspaceLockfilePath = (scope) => scope === "project" ? "axm-lock.yaml" : ".axm/workspace/axm-lock.yaml";
|
|
15
|
+
/** The root every acquired package sits under, relative to the workspace root. */
|
|
16
|
+
export const workspaceCanonicalRoot = (scope) => scope === "project" ? "agent_extensions" : ".axm/workspace/agent_extensions";
|
|
17
|
+
/** One acquired package's path, relative to the workspace root. */
|
|
18
|
+
export const workspaceCanonicalPath = (scope, relativePath) => `${workspaceCanonicalRoot(scope)}/${relativePath.replace(/^\/+/, "")}`;
|
|
19
|
+
const extensionDirectory = {
|
|
20
|
+
skill: "skills",
|
|
21
|
+
"mcp-server": "mcps",
|
|
22
|
+
subagent: "subagents",
|
|
23
|
+
rule: "rules",
|
|
24
|
+
hook: "hooks",
|
|
25
|
+
knowledge: "knowledge",
|
|
26
|
+
pack: "packs",
|
|
27
|
+
};
|
|
28
|
+
/** Where one desired-graph node's package sits, authored or acquired. */
|
|
29
|
+
export const workspaceCanonicalNodePath = (path, ws, node) => {
|
|
30
|
+
if (node.identity.startsWith("workspace:") && ws.layout.scope === "project") {
|
|
31
|
+
return path.join(path.relative(ws.baseDir, ws.layout.authoredRoot(node.type)), node.name);
|
|
32
|
+
}
|
|
33
|
+
const identity = node.identity.startsWith("workspace:")
|
|
34
|
+
? node.identity.slice("workspace:".length)
|
|
35
|
+
: node.identity;
|
|
36
|
+
return workspaceCanonicalPath(ws.scope, identity);
|
|
37
|
+
};
|
|
38
|
+
/** Where a workspace-authored package of this type and name sits. */
|
|
39
|
+
export const workspaceAuthoredPath = (path, ws, type, name) => {
|
|
40
|
+
if (ws.layout.scope === "project") {
|
|
41
|
+
return path.join(path.relative(ws.baseDir, ws.layout.authoredRoot(type)), name);
|
|
42
|
+
}
|
|
43
|
+
const owner = ws.layout.owner ?? "@workspace";
|
|
44
|
+
return workspaceCanonicalPath(ws.scope, `${owner}/${extensionDirectory[type]}/${name}`);
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=workspace-paths.js.map
|