@agentxm/extension-materialization 0.28.14-preview.1789139351.5cd4595aa
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/LICENSE +110 -0
- package/README.md +16 -0
- package/dist/src/desired-state/errors.d.ts +22 -0
- package/dist/src/desired-state/errors.js +22 -0
- package/dist/src/desired-state/retained-materialization.d.ts +58 -0
- package/dist/src/desired-state/retained-materialization.js +119 -0
- package/dist/src/errors.d.ts +42 -0
- package/dist/src/errors.js +14 -0
- package/dist/src/extensions/canonical-directory.d.ts +106 -0
- package/dist/src/extensions/canonical-directory.js +210 -0
- package/dist/src/extensions/canonical-reuse.d.ts +43 -0
- package/dist/src/extensions/canonical-reuse.js +38 -0
- package/dist/src/extensions/copy-directory.d.ts +50 -0
- package/dist/src/extensions/copy-directory.js +78 -0
- package/dist/src/extensions/errors.d.ts +97 -0
- package/dist/src/extensions/errors.js +42 -0
- package/dist/src/extensions/materializable-from-disk.d.ts +37 -0
- package/dist/src/extensions/materializable-from-disk.js +89 -0
- package/dist/src/extensions/operations.d.ts +252 -0
- package/dist/src/extensions/operations.js +631 -0
- package/dist/src/hooks/errors.d.ts +33 -0
- package/dist/src/hooks/errors.js +18 -0
- package/dist/src/hooks/manager.d.ts +13 -0
- package/dist/src/hooks/manager.js +778 -0
- package/dist/src/index.d.ts +44 -0
- package/dist/src/index.js +56 -0
- package/dist/src/knowledge/errors.d.ts +75 -0
- package/dist/src/knowledge/errors.js +36 -0
- package/dist/src/knowledge/manager.d.ts +9 -0
- package/dist/src/knowledge/manager.js +709 -0
- package/dist/src/live.d.ts +20 -0
- package/dist/src/live.js +20 -0
- package/dist/src/manager-contract.d.ts +136 -0
- package/dist/src/manager-contract.js +19 -0
- package/dist/src/manager-registry-live.d.ts +11 -0
- package/dist/src/manager-registry-live.js +23 -0
- package/dist/src/manager-registry.d.ts +37 -0
- package/dist/src/manager-registry.js +22 -0
- package/dist/src/managers.d.ts +147 -0
- package/dist/src/managers.js +32 -0
- package/dist/src/mcps/artifact.d.ts +24 -0
- package/dist/src/mcps/artifact.js +74 -0
- package/dist/src/mcps/authored-materialization.d.ts +28 -0
- package/dist/src/mcps/authored-materialization.js +34 -0
- package/dist/src/mcps/errors.d.ts +105 -0
- package/dist/src/mcps/errors.js +40 -0
- package/dist/src/mcps/install-operation.d.ts +92 -0
- package/dist/src/mcps/install-operation.js +557 -0
- package/dist/src/mcps/manager.d.ts +15 -0
- package/dist/src/mcps/manager.js +253 -0
- package/dist/src/mcps/native-entry.d.ts +51 -0
- package/dist/src/mcps/native-entry.js +100 -0
- package/dist/src/mcps/secret-store-live.d.ts +14 -0
- package/dist/src/mcps/secret-store-live.js +58 -0
- package/dist/src/mcps/secret-store.d.ts +55 -0
- package/dist/src/mcps/secret-store.js +33 -0
- package/dist/src/packs/errors.d.ts +50 -0
- package/dist/src/packs/errors.js +24 -0
- package/dist/src/packs/manager.d.ts +16 -0
- package/dist/src/packs/manager.js +189 -0
- package/dist/src/projection-participants-live.d.ts +17 -0
- package/dist/src/projection-participants-live.js +66 -0
- package/dist/src/projection-step-failure.d.ts +19 -0
- package/dist/src/projection-step-failure.js +76 -0
- package/dist/src/registry-materialization.d.ts +52 -0
- package/dist/src/registry-materialization.js +67 -0
- package/dist/src/rules/errors.d.ts +31 -0
- package/dist/src/rules/errors.js +17 -0
- package/dist/src/rules/manager.d.ts +18 -0
- package/dist/src/rules/manager.js +492 -0
- package/dist/src/skills/errors.d.ts +42 -0
- package/dist/src/skills/errors.js +21 -0
- package/dist/src/skills/manager.d.ts +17 -0
- package/dist/src/skills/manager.js +339 -0
- package/dist/src/skills/materialization.d.ts +52 -0
- package/dist/src/skills/materialization.js +168 -0
- package/dist/src/skills/skill-artifact.d.ts +38 -0
- package/dist/src/skills/skill-artifact.js +70 -0
- package/dist/src/skills/source-hash.d.ts +2 -0
- package/dist/src/skills/source-hash.js +6 -0
- package/dist/src/subagents/errors.d.ts +43 -0
- package/dist/src/subagents/errors.js +20 -0
- package/dist/src/subagents/lock-entry-builder.d.ts +20 -0
- package/dist/src/subagents/lock-entry-builder.js +63 -0
- package/dist/src/subagents/manager.d.ts +17 -0
- package/dist/src/subagents/manager.js +736 -0
- package/dist/src/testing.d.ts +87 -0
- package/dist/src/testing.js +116 -0
- package/package.json +70 -0
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
// @effect-diagnostics anyUnknownInErrorContext:off — schema and filesystem errors are swept into KnowledgeIoFailed inside this manager
|
|
2
|
+
/** Lifecycle manager for isolated Open Knowledge Format bundles. */
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import { stripFileProtocol } from "@agentxm/registry-client";
|
|
5
|
+
import * as FileSystem from "effect/FileSystem";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
import * as Path from "effect/Path";
|
|
9
|
+
import * as Ref from "effect/Ref";
|
|
10
|
+
import * as Schema from "effect/Schema";
|
|
11
|
+
import * as Scope from "effect/Scope";
|
|
12
|
+
import * as Result from "effect/Result";
|
|
13
|
+
import { PlatformError } from "effect/PlatformError";
|
|
14
|
+
import { KnowledgeDefinitionInvalid, KnowledgeDesiredStateUnreconcilable, KnowledgeInstallStateMissing, KnowledgeIoFailed, KnowledgeObservableContractViolated, KnowledgeResolutionMissing, KnowledgeUnavailable, } from "./errors.js";
|
|
15
|
+
import { applyProjectionPlans, formatProjectionExclusions, planAggregateProjection, requireCompleteGraph, KNOWLEDGE_REGION_OWNER, reconcileKnowledgeDiscovery, resolveInstructionsConfig, } from "@agentxm/workspace-projection";
|
|
16
|
+
import { canReuseInstalledPackage, materializeExternalPackage, } from "../extensions/canonical-directory.js";
|
|
17
|
+
import { materializeRegistryPackage } from "../registry-materialization.js";
|
|
18
|
+
import { computeExtensionPathsForLayout, extensionPathSourceFromLockEntry, } from "@agentxm/workspace-state";
|
|
19
|
+
import { computePackageContentHash } from "@agentxm/workspace-state";
|
|
20
|
+
import { computeMaterializedTreeIntegrity } from "@agentxm/workspace-state";
|
|
21
|
+
import { acceptedRegistryVersionForRef, validateExactResolvedVersion, } from "@agentxm/workspace-state";
|
|
22
|
+
import { gitSourceLockFields } from "@agentxm/workspace-state";
|
|
23
|
+
import { SourceHostProviders, WorkspaceCatalog } from "@agentxm/extension-sources";
|
|
24
|
+
import { knowledgeLockEntryToRef } from "@agentxm/workspace-state";
|
|
25
|
+
import { makeWorkspaceRelativeSourcePath } from "@agentxm/extension-model/unstable/path-types";
|
|
26
|
+
import { recordFootprint } from "@agentxm/workspace-transactions";
|
|
27
|
+
import { makeWorkspaceRelativePath } from "@agentxm/extension-model/unstable/path-types";
|
|
28
|
+
import { decodeVersionSync } from "@agentxm/extension-model/unstable/version-constraints";
|
|
29
|
+
import { usableAcceptedCanonicalRef } from "@agentxm/workspace-state";
|
|
30
|
+
import { getKnowledgeLockEntries } from "@agentxm/workspace-state";
|
|
31
|
+
import { NO_MATERIALIZATION_OBSERVATION } from "../manager-contract.js";
|
|
32
|
+
import { KnowledgeManager, } from "../managers.js";
|
|
33
|
+
import { WorkspaceMutations } from "@agentxm/workspace-state";
|
|
34
|
+
import { runWorkspaceTransaction } from "@agentxm/workspace-transactions";
|
|
35
|
+
import { isObservedInstalled } from "@agentxm/workspace-state";
|
|
36
|
+
import { acceptedCanonicalObservation, prepareAcceptedCanonicalTransition, removableAcceptedCanonicalPath, } from "@agentxm/workspace-state";
|
|
37
|
+
import { protectWorkspacePath } from "@agentxm/workspace-transactions";
|
|
38
|
+
import { isSourcedDesiredExtension } from "@agentxm/workspace-state";
|
|
39
|
+
import { KNOWLEDGE_EXTENSION_DIR, KNOWLEDGE_MANIFEST_FILENAME, KNOWLEDGE_SOURCE_DIR, KnowledgeManifestSchema, } from "@agentxm/extension-model/unstable/knowledge/manifest-schema";
|
|
40
|
+
import { inspectKnowledgeBundle, } from "@agentxm/extension-content/knowledge";
|
|
41
|
+
import { resolveKnowledgeInstructionEntry } from "@agentxm/workspace-projection";
|
|
42
|
+
const decodeManifest = Schema.decodeUnknownEffect(KnowledgeManifestSchema);
|
|
43
|
+
const registryLockEntry = (ref, treeIntegrity) => ({
|
|
44
|
+
type: "registry",
|
|
45
|
+
sourceType: "registry",
|
|
46
|
+
packageFormat: "agentxm",
|
|
47
|
+
endpoint: ref.source.location,
|
|
48
|
+
extensionType: "knowledge",
|
|
49
|
+
workspaceName: ref.knowledge.name,
|
|
50
|
+
owner: ref.owner,
|
|
51
|
+
name: ref.name,
|
|
52
|
+
resolvedVersion: decodeVersionSync(ref.version),
|
|
53
|
+
integrity: Option.getOrElse(ref.integrity, () => ""),
|
|
54
|
+
sourceName: ref.source.name,
|
|
55
|
+
publisherBindingId: ref.publisherBindingId,
|
|
56
|
+
treeIntegrity,
|
|
57
|
+
});
|
|
58
|
+
const gitLockEntry = (ref, contentIdentity, treeIntegrity) => ({
|
|
59
|
+
...gitSourceLockFields(ref.source, "knowledge", ref.knowledge.name, Option.fromUndefinedOr(ref.sourcePath), ref.gitCommitSha, ref.gitTreeSha, contentIdentity, ref.owner, ref.name, treeIntegrity),
|
|
60
|
+
});
|
|
61
|
+
const localLockEntry = (ref, relativePath, contentIdentity, treeIntegrity) => ({
|
|
62
|
+
type: "local",
|
|
63
|
+
sourceType: "local",
|
|
64
|
+
sourceName: "local",
|
|
65
|
+
extensionType: "knowledge",
|
|
66
|
+
workspaceName: ref.knowledge.name,
|
|
67
|
+
packageFormat: "agentxm",
|
|
68
|
+
packageOwner: ref.owner,
|
|
69
|
+
packageName: ref.name,
|
|
70
|
+
path: Option.getOrElse(relativePath, () => ref.source.path),
|
|
71
|
+
contentIdentity,
|
|
72
|
+
treeIntegrity,
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* Name the failure that stopped a restore in the operator's sentence: the
|
|
76
|
+
* producing family carries its own detail, so no application envelope is
|
|
77
|
+
* needed to describe it.
|
|
78
|
+
*/
|
|
79
|
+
const describeKnowledgeFailure = (failure) => "detail" in failure && typeof failure.detail === "string" ? failure.detail : failure._tag;
|
|
80
|
+
export const KnowledgeManagerLive = Layer.effect(KnowledgeManager, Effect.gen(function* () {
|
|
81
|
+
const ws = yield* WorkspaceMutations;
|
|
82
|
+
const fs = yield* FileSystem.FileSystem;
|
|
83
|
+
const path = yield* Path.Path;
|
|
84
|
+
const sources = yield* SourceHostProviders;
|
|
85
|
+
const catalog = yield* WorkspaceCatalog;
|
|
86
|
+
const baseDir = ws.baseDir;
|
|
87
|
+
// The workspace facade and the source integration are this layer's own
|
|
88
|
+
// dependencies; the platform stays in `R` for every member.
|
|
89
|
+
const provide = (effect) => effect.pipe(Effect.provideService(WorkspaceMutations, ws), Effect.provideService(SourceHostProviders, sources), Effect.provideService(WorkspaceCatalog, catalog));
|
|
90
|
+
const acquiredFacts = (prepared, relativeLocalSource) => ({
|
|
91
|
+
observation: NO_MATERIALIZATION_OBSERVATION,
|
|
92
|
+
acquired: Option.some({
|
|
93
|
+
relativeLocalSource,
|
|
94
|
+
sourceHash: prepared.sourceHash,
|
|
95
|
+
...(prepared.treeIntegrity === undefined ? {} : { treeIntegrity: prepared.treeIntegrity }),
|
|
96
|
+
}),
|
|
97
|
+
});
|
|
98
|
+
const withdrawn = {
|
|
99
|
+
observation: NO_MATERIALIZATION_OBSERVATION,
|
|
100
|
+
acquired: Option.none(),
|
|
101
|
+
};
|
|
102
|
+
const canonicalPathForRef = (ref) => computeExtensionPathsForLayout(path.join, ws.layout, ref, KNOWLEDGE_EXTENSION_DIR, ref.name)
|
|
103
|
+
.canonicalPath;
|
|
104
|
+
const materializePackage = (ref, options) => {
|
|
105
|
+
const canonicalPath = options?.destinationPath ?? canonicalPathForRef(ref);
|
|
106
|
+
const materializationBaseDir = options?.baseDir ?? baseDir;
|
|
107
|
+
switch (ref.refType) {
|
|
108
|
+
case "registry":
|
|
109
|
+
return Effect.gen(function* () {
|
|
110
|
+
return yield* provide(materializeRegistryPackage({
|
|
111
|
+
baseDir: materializationBaseDir,
|
|
112
|
+
destinationPath: canonicalPath,
|
|
113
|
+
sourceLocation: ref.source.location,
|
|
114
|
+
owner: ref.owner,
|
|
115
|
+
type: "knowledge",
|
|
116
|
+
name: ref.name,
|
|
117
|
+
version: ref.version,
|
|
118
|
+
integrity: ref.integrity,
|
|
119
|
+
messages: {
|
|
120
|
+
integrityMismatchDetail: `Integrity mismatch for knowledge:${ref.name}@${ref.version}`,
|
|
121
|
+
},
|
|
122
|
+
}));
|
|
123
|
+
});
|
|
124
|
+
case "git-hosted":
|
|
125
|
+
case "local":
|
|
126
|
+
return provide(materializeExternalPackage({
|
|
127
|
+
baseDir: materializationBaseDir,
|
|
128
|
+
canonicalPath,
|
|
129
|
+
sourceLocation: ref.location,
|
|
130
|
+
copyFailureCode: "validation",
|
|
131
|
+
copyFailureDetail: (target) => `Failed to copy knowledge package to ${target}`,
|
|
132
|
+
}));
|
|
133
|
+
case "workspace":
|
|
134
|
+
if (ref.scope !== ws.scope ||
|
|
135
|
+
path.resolve(ref.location) !== path.resolve(canonicalPath)) {
|
|
136
|
+
return Effect.fail(new KnowledgeDefinitionInvalid({
|
|
137
|
+
detail: `Invalid workspace knowledge source location: ${ref.location}`,
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
return Effect.succeed(ref.location);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const inspectPackage = (packageRoot) => Effect.gen(function* () {
|
|
144
|
+
const raw = yield* fs
|
|
145
|
+
.readFileString(path.join(packageRoot, KNOWLEDGE_MANIFEST_FILENAME))
|
|
146
|
+
.pipe(Effect.mapError((cause) => new KnowledgeDefinitionInvalid({
|
|
147
|
+
detail: `Failed to read ${KNOWLEDGE_MANIFEST_FILENAME}`,
|
|
148
|
+
cause,
|
|
149
|
+
})));
|
|
150
|
+
const manifest = yield* Effect.try({
|
|
151
|
+
try: () => JSON.parse(raw),
|
|
152
|
+
catch: (cause) => new KnowledgeDefinitionInvalid({
|
|
153
|
+
detail: `Failed to parse ${KNOWLEDGE_MANIFEST_FILENAME}`,
|
|
154
|
+
cause,
|
|
155
|
+
}),
|
|
156
|
+
}).pipe(Effect.flatMap(decodeManifest), Effect.mapError((cause) => new KnowledgeDefinitionInvalid({
|
|
157
|
+
detail: `Invalid ${KNOWLEDGE_MANIFEST_FILENAME}`,
|
|
158
|
+
cause,
|
|
159
|
+
})));
|
|
160
|
+
const inspection = yield* provide(inspectKnowledgeBundle(path.join(packageRoot, KNOWLEDGE_SOURCE_DIR))).pipe(Effect.mapError((cause) => new KnowledgeDefinitionInvalid({
|
|
161
|
+
detail: "Failed to inspect Open Knowledge Format bundle",
|
|
162
|
+
cause,
|
|
163
|
+
})));
|
|
164
|
+
const errors = inspection.diagnostics.filter((item) => item.severity === "error");
|
|
165
|
+
if (errors.length > 0) {
|
|
166
|
+
return yield* new KnowledgeDefinitionInvalid({
|
|
167
|
+
detail: errors
|
|
168
|
+
.map((item) => item.details?.kind === "frontmatter-parse"
|
|
169
|
+
? `${item.relativePath}: ${item.message}`
|
|
170
|
+
: item.message)
|
|
171
|
+
.join(" "),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return { manifest, inspection };
|
|
175
|
+
});
|
|
176
|
+
const preparePackage = (ref, force = false) => Effect.gen(function* () {
|
|
177
|
+
const canonicalPath = canonicalPathForRef(ref);
|
|
178
|
+
if (ref.refType === "workspace") {
|
|
179
|
+
const root = yield* materializePackage(ref);
|
|
180
|
+
yield* inspectPackage(root);
|
|
181
|
+
return {
|
|
182
|
+
root,
|
|
183
|
+
sourceHash: ref.sourceHash,
|
|
184
|
+
commit: Effect.void,
|
|
185
|
+
rollback: Effect.void,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// Decide against the canonical tree before staging: the staged path
|
|
189
|
+
// never exists, so a decision made there would re-extract every time
|
|
190
|
+
// and revert workspace-owned content on a no-op install.
|
|
191
|
+
if (ref.refType === "registry") {
|
|
192
|
+
const lockedEntry = yield* ws.getLockedKnowledgeEntry(ref.knowledge.name);
|
|
193
|
+
const lockedVersion = acceptedRegistryVersionForRef(lockedEntry, ref);
|
|
194
|
+
const reuse = yield* provide(canReuseInstalledPackage({
|
|
195
|
+
installedPath: canonicalPath,
|
|
196
|
+
force,
|
|
197
|
+
refVersion: ref.version,
|
|
198
|
+
hasIntegrity: Option.isSome(ref.integrity),
|
|
199
|
+
...(lockedVersion === undefined ? {} : { lockedVersion }),
|
|
200
|
+
existsFailureDetail: (target) => `Failed to inspect knowledge path: ${target}`,
|
|
201
|
+
}));
|
|
202
|
+
if (reuse && Option.isSome(lockedEntry)) {
|
|
203
|
+
const observedTree = yield* provide(computeMaterializedTreeIntegrity(canonicalPath));
|
|
204
|
+
if (observedTree !== lockedEntry.value.treeIntegrity) {
|
|
205
|
+
yield* Effect.logWarning(`Knowledge package ${ref.knowledge.name} differs from its accepted tree; rematerializing`);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
return {
|
|
209
|
+
root: canonicalPath,
|
|
210
|
+
sourceHash: yield* provide(computePackageContentHash(canonicalPath)),
|
|
211
|
+
treeIntegrity: lockedEntry.value.treeIntegrity,
|
|
212
|
+
commit: Effect.void,
|
|
213
|
+
rollback: Effect.void,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const tempDir = yield* fs.makeTempDirectoryScoped({ prefix: "axm-knowledge-package-" });
|
|
219
|
+
const stagedPath = path.join(tempDir, "staged");
|
|
220
|
+
const backupPath = path.join(tempDir, "previous");
|
|
221
|
+
const stageState = yield* Ref.make({ phase: "preparing" });
|
|
222
|
+
const restoreStagedPackage = Ref.get(stageState).pipe(Effect.flatMap((state) => {
|
|
223
|
+
if (state.phase !== "staged")
|
|
224
|
+
return Effect.void;
|
|
225
|
+
return fs.remove(canonicalPath, { recursive: true, force: true }).pipe(Effect.mapError((cause) => new KnowledgeIoFailed({
|
|
226
|
+
detail: `Failed to remove staged Knowledge package during rollback: ${canonicalPath}`,
|
|
227
|
+
cause,
|
|
228
|
+
})), Effect.andThen(state.hadCanonical
|
|
229
|
+
? fs.rename(backupPath, canonicalPath).pipe(Effect.mapError((cause) => new KnowledgeIoFailed({
|
|
230
|
+
detail: `Failed to restore Knowledge package during rollback: ${canonicalPath}`,
|
|
231
|
+
cause,
|
|
232
|
+
})))
|
|
233
|
+
: Effect.void), Effect.andThen(Ref.set(stageState, { phase: "settled" })));
|
|
234
|
+
}));
|
|
235
|
+
yield* Effect.addFinalizer(() => restoreStagedPackage.pipe(Effect.catchCause((cause) => Effect.logError("Failed to restore staged Knowledge package during finalization", cause))));
|
|
236
|
+
const stagedRoot = yield* materializePackage(ref, {
|
|
237
|
+
baseDir: tempDir,
|
|
238
|
+
destinationPath: stagedPath,
|
|
239
|
+
});
|
|
240
|
+
yield* inspectPackage(stagedRoot);
|
|
241
|
+
const sourceHash = yield* provide(computePackageContentHash(stagedRoot));
|
|
242
|
+
const treeIntegrity = yield* provide(computeMaterializedTreeIntegrity(stagedRoot));
|
|
243
|
+
yield* protectWorkspacePath(canonicalPath);
|
|
244
|
+
const hadCanonical = yield* fs.exists(canonicalPath);
|
|
245
|
+
yield* Effect.uninterruptible(Effect.gen(function* () {
|
|
246
|
+
if (hadCanonical)
|
|
247
|
+
yield* fs.rename(canonicalPath, backupPath);
|
|
248
|
+
yield* fs.makeDirectory(path.dirname(canonicalPath), { recursive: true });
|
|
249
|
+
yield* fs.rename(stagedPath, canonicalPath).pipe(Effect.tapError(() => hadCanonical
|
|
250
|
+
? fs.rename(backupPath, canonicalPath).pipe(Effect.mapError((cause) => new KnowledgeIoFailed({
|
|
251
|
+
detail: `Failed to restore Knowledge package after staging failed: ${canonicalPath}`,
|
|
252
|
+
cause,
|
|
253
|
+
})))
|
|
254
|
+
: Effect.void));
|
|
255
|
+
yield* Ref.set(stageState, { phase: "staged", hadCanonical });
|
|
256
|
+
}));
|
|
257
|
+
yield* recordFootprint({
|
|
258
|
+
path: canonicalPath,
|
|
259
|
+
change: hadCanonical ? "modified" : "created",
|
|
260
|
+
});
|
|
261
|
+
return {
|
|
262
|
+
root: canonicalPath,
|
|
263
|
+
sourceHash,
|
|
264
|
+
treeIntegrity,
|
|
265
|
+
commit: Ref.set(stageState, { phase: "settled" }),
|
|
266
|
+
rollback: restoreStagedPackage,
|
|
267
|
+
};
|
|
268
|
+
}).pipe(Effect.mapError((cause) => cause instanceof PlatformError
|
|
269
|
+
? new KnowledgeIoFailed({
|
|
270
|
+
detail: `Failed to stage Knowledge bundle ${ref.knowledge.name}`,
|
|
271
|
+
cause,
|
|
272
|
+
})
|
|
273
|
+
: cause));
|
|
274
|
+
const getInstructionsTarget = () => Effect.gen(function* () {
|
|
275
|
+
const config = yield* ws.getInstructionsConfig();
|
|
276
|
+
const enabled = Option.isSome(config) && config.value !== false;
|
|
277
|
+
const resolved = resolveInstructionsConfig(enabled ? config.value : undefined);
|
|
278
|
+
const relative = makeWorkspaceRelativePath(path, baseDir, resolved.fileName);
|
|
279
|
+
if (Option.isNone(relative)) {
|
|
280
|
+
return yield* new KnowledgeDefinitionInvalid({
|
|
281
|
+
detail: `Knowledge discovery instruction target escapes workspace: ${resolved.fileName}`,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
path: path.resolve(baseDir, relative.value),
|
|
286
|
+
enabled,
|
|
287
|
+
preserveSource: enabled,
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
const canonicalRoot = (_name, locked) => computeExtensionPathsForLayout(path.join, ws.layout, extensionPathSourceFromLockEntry(locked), KNOWLEDGE_EXTENSION_DIR, locked.workspaceName).canonicalPath;
|
|
291
|
+
const desiredCanonicalRoot = (node, locked) => {
|
|
292
|
+
if (node.identity.startsWith("workspace:")) {
|
|
293
|
+
return ws.layout.scope === "project"
|
|
294
|
+
? Effect.succeed(path.join(ws.layout.authoredRoot("knowledge"), node.name))
|
|
295
|
+
: Effect.fail(new KnowledgeDefinitionInvalid({
|
|
296
|
+
detail: "User workspaces do not support workspace-authored Knowledge bundles",
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
return locked === undefined
|
|
300
|
+
? Effect.fail(new KnowledgeResolutionMissing({ name: node.name }))
|
|
301
|
+
: Effect.succeed(canonicalRoot(node.name, locked));
|
|
302
|
+
};
|
|
303
|
+
const toProjectionBundle = (root, inspected) => ({
|
|
304
|
+
owner: inspected.manifest.owner,
|
|
305
|
+
name: inspected.manifest.name,
|
|
306
|
+
sourceDir: path.join(root, KNOWLEDGE_SOURCE_DIR),
|
|
307
|
+
...(inspected.manifest.description === undefined
|
|
308
|
+
? {}
|
|
309
|
+
: { description: inspected.manifest.description }),
|
|
310
|
+
});
|
|
311
|
+
const activeKnowledgeNodes = () => ws
|
|
312
|
+
.getDesiredStateGraph()
|
|
313
|
+
.pipe(Effect.flatMap((graph) => graph.complete
|
|
314
|
+
? Effect.succeed(graph.nodes
|
|
315
|
+
.filter(isSourcedDesiredExtension)
|
|
316
|
+
.filter((node) => node.type === "knowledge" && node.enabled))
|
|
317
|
+
: new KnowledgeDesiredStateUnreconcilable()));
|
|
318
|
+
/**
|
|
319
|
+
* Classify a bundle whose package could not be inspected. Only the
|
|
320
|
+
* bundle's own content is in question here: resolution facts have already
|
|
321
|
+
* succeeded, so the two truthful answers are that the package is gone or
|
|
322
|
+
* that its content is invalid.
|
|
323
|
+
*/
|
|
324
|
+
const excludedContributor = (name, root, failure) => Effect.gen(function* () {
|
|
325
|
+
const probe = yield* Effect.result(fs.exists(path.join(root, KNOWLEDGE_MANIFEST_FILENAME)));
|
|
326
|
+
const manifestPresent = Result.isSuccess(probe) && probe.success;
|
|
327
|
+
if (!manifestPresent)
|
|
328
|
+
return { contributor: name, reason: "package-missing" };
|
|
329
|
+
return {
|
|
330
|
+
contributor: name,
|
|
331
|
+
reason: "package-invalid",
|
|
332
|
+
detail: failure._tag === "KnowledgeDefinitionInvalid"
|
|
333
|
+
? failure.detail
|
|
334
|
+
: describeKnowledgeFailure(failure),
|
|
335
|
+
};
|
|
336
|
+
});
|
|
337
|
+
/**
|
|
338
|
+
* Resolve the contributor set for the discovery region. A bundle that
|
|
339
|
+
* fails inspection cannot supply a row, so it is excluded and reported
|
|
340
|
+
* rather than failing every other bundle's command with it. Resolution
|
|
341
|
+
* failures — a missing lock entry, an unsupported workspace-authored
|
|
342
|
+
* bundle — remain fail-closed: they are AXM state problems, not bundle
|
|
343
|
+
* content problems.
|
|
344
|
+
*/
|
|
345
|
+
const selectKnowledgeBundles = (graph, locked, configured, config, instructionFilesEnabled) => Effect.forEach(graph.nodes.filter((node) => node.type === "knowledge" && node.enabled), (node) => Effect.gen(function* () {
|
|
346
|
+
const root = yield* desiredCanonicalRoot(node, locked[node.name]);
|
|
347
|
+
const workspaceInstructionEntry = configured[node.name]?.instructionEntry;
|
|
348
|
+
const resolveInclusion = (manifestInstructionEntry) => resolveKnowledgeInstructionEntry({
|
|
349
|
+
bundleEnabled: node.enabled,
|
|
350
|
+
instructionFilesEnabled,
|
|
351
|
+
knowledgeInstructionsEnabled: config.instructions,
|
|
352
|
+
...(workspaceInstructionEntry === undefined ? {} : { workspaceInstructionEntry }),
|
|
353
|
+
...(manifestInstructionEntry === undefined ? {} : { manifestInstructionEntry }),
|
|
354
|
+
});
|
|
355
|
+
const inspection = yield* Effect.result(inspectPackage(root));
|
|
356
|
+
if (Result.isFailure(inspection)) {
|
|
357
|
+
// A bundle the workspace would not publish anyway is simply
|
|
358
|
+
// absent from the region; reporting it would be noise.
|
|
359
|
+
return resolveInclusion().included
|
|
360
|
+
? {
|
|
361
|
+
contributors: [],
|
|
362
|
+
exclusions: [yield* excludedContributor(node.name, root, inspection.failure)],
|
|
363
|
+
}
|
|
364
|
+
: { contributors: [], exclusions: [] };
|
|
365
|
+
}
|
|
366
|
+
const inspected = inspection.success;
|
|
367
|
+
const resolution = resolveInclusion(inspected.manifest.instructionEntry);
|
|
368
|
+
return {
|
|
369
|
+
contributors: resolution.included ? [toProjectionBundle(root, inspected)] : [],
|
|
370
|
+
exclusions: [],
|
|
371
|
+
};
|
|
372
|
+
})).pipe(Effect.map((selections) => ({
|
|
373
|
+
contributors: selections.flatMap(({ contributors }) => contributors),
|
|
374
|
+
exclusions: selections.flatMap(({ exclusions }) => exclusions),
|
|
375
|
+
})));
|
|
376
|
+
const resolveKnowledgeProjection = () => Effect.gen(function* () {
|
|
377
|
+
const graph = yield* ws.getDesiredStateGraph();
|
|
378
|
+
const locked = yield* getKnowledgeLockEntries(ws);
|
|
379
|
+
const configured = yield* ws.getConfiguredKnowledgeEntries();
|
|
380
|
+
const config = yield* ws.getKnowledgeDiscoveryConfig();
|
|
381
|
+
const instructionsTarget = yield* getInstructionsTarget();
|
|
382
|
+
return { graph, locked, configured, config, instructionsTarget };
|
|
383
|
+
});
|
|
384
|
+
const runKnowledgeProjectionAdapter = (args) => provide(reconcileKnowledgeDiscovery({
|
|
385
|
+
scopeRoot: baseDir,
|
|
386
|
+
config: args.config,
|
|
387
|
+
bundles: args.bundles,
|
|
388
|
+
instructionsPath: args.instructionsTarget.path,
|
|
389
|
+
instructionManagementEnabled: args.instructionsTarget.enabled,
|
|
390
|
+
preserveInstructionsSource: args.instructionsTarget.preserveSource,
|
|
391
|
+
...(args.dryRun === undefined ? {} : { dryRun: args.dryRun }),
|
|
392
|
+
}));
|
|
393
|
+
const makeKnowledgeProjectionPlan = () => Effect.gen(function* () {
|
|
394
|
+
const { graph, locked, configured, config, instructionsTarget } = yield* resolveKnowledgeProjection();
|
|
395
|
+
return yield* planAggregateProjection({
|
|
396
|
+
unitId: "knowledge:discovery-region",
|
|
397
|
+
targetFile: instructionsTarget.path,
|
|
398
|
+
graph,
|
|
399
|
+
select: (completeGraph) => selectKnowledgeBundles(completeGraph, locked, configured, config, instructionsTarget.enabled),
|
|
400
|
+
adapter: {
|
|
401
|
+
observe: (input) => runKnowledgeProjectionAdapter({
|
|
402
|
+
bundles: input.contributors,
|
|
403
|
+
config,
|
|
404
|
+
instructionsTarget,
|
|
405
|
+
dryRun: true,
|
|
406
|
+
}).pipe(Effect.map((result) => ({
|
|
407
|
+
unitId: "knowledge:discovery-region",
|
|
408
|
+
path: `${instructionsTarget.path}#knowledge`,
|
|
409
|
+
owner: KNOWLEDGE_REGION_OWNER,
|
|
410
|
+
present: Option.isSome(result.observedRegion),
|
|
411
|
+
current: !result.changed,
|
|
412
|
+
expectedContributors: input.contributors.map(({ owner, name }) => `${owner}/knowledge/${name}`),
|
|
413
|
+
}))),
|
|
414
|
+
apply: (input) => runKnowledgeProjectionAdapter({
|
|
415
|
+
bundles: input.contributors,
|
|
416
|
+
config,
|
|
417
|
+
instructionsTarget,
|
|
418
|
+
}).pipe(Effect.asVoid),
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
const projectionPlans = () => makeKnowledgeProjectionPlan().pipe(Effect.map((plan) => [plan]));
|
|
423
|
+
const applyKnowledgeProjection = projectionPlans().pipe(Effect.flatMap(applyProjectionPlans));
|
|
424
|
+
const reconcileDiscovery = (options) => resolveKnowledgeProjection().pipe(Effect.flatMap(({ graph, locked, configured, config, instructionsTarget }) => requireCompleteGraph(graph).pipe(Effect.flatMap((completeGraph) => selectKnowledgeBundles(completeGraph, locked, configured, config, instructionsTarget.enabled)), Effect.flatMap(({ contributors, exclusions }) => runKnowledgeProjectionAdapter({
|
|
425
|
+
bundles: contributors,
|
|
426
|
+
config,
|
|
427
|
+
instructionsTarget,
|
|
428
|
+
...(options?.dryRun === undefined ? {} : { dryRun: options.dryRun }),
|
|
429
|
+
}).pipe(Effect.map((result) => ({
|
|
430
|
+
...result,
|
|
431
|
+
warnings: formatProjectionExclusions({
|
|
432
|
+
exclusions,
|
|
433
|
+
targetFile: instructionsTarget.path,
|
|
434
|
+
}),
|
|
435
|
+
})))))));
|
|
436
|
+
const buildLockEntry = (ref, materialization) => Effect.gen(function* () {
|
|
437
|
+
const state = Option.getOrUndefined(materialization.pipe(Option.flatMap((facts) => facts.acquired)));
|
|
438
|
+
switch (ref.refType) {
|
|
439
|
+
case "registry":
|
|
440
|
+
return state?.treeIntegrity === undefined
|
|
441
|
+
? yield* new KnowledgeInstallStateMissing({
|
|
442
|
+
name: ref.knowledge.name,
|
|
443
|
+
kind: "tree-integrity",
|
|
444
|
+
})
|
|
445
|
+
: Option.some(registryLockEntry(ref, state.treeIntegrity));
|
|
446
|
+
case "git-hosted":
|
|
447
|
+
return state?.treeIntegrity === undefined
|
|
448
|
+
? yield* new KnowledgeInstallStateMissing({
|
|
449
|
+
name: ref.knowledge.name,
|
|
450
|
+
kind: "content-identity",
|
|
451
|
+
})
|
|
452
|
+
: Option.some(gitLockEntry(ref, state.sourceHash, state.treeIntegrity));
|
|
453
|
+
case "local":
|
|
454
|
+
return state?.treeIntegrity === undefined
|
|
455
|
+
? yield* new KnowledgeInstallStateMissing({
|
|
456
|
+
name: ref.knowledge.name,
|
|
457
|
+
kind: "content-identity",
|
|
458
|
+
})
|
|
459
|
+
: Option.some(localLockEntry(ref, state.relativeLocalSource, state.sourceHash, state.treeIntegrity));
|
|
460
|
+
case "workspace":
|
|
461
|
+
return Option.none();
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
const setKnowledgeSourceEntry = (name, source) => Effect.gen(function* () {
|
|
465
|
+
const configured = yield* ws.getConfiguredKnowledgeEntries();
|
|
466
|
+
const current = configured[name];
|
|
467
|
+
yield* ws.setKnowledgeEntry(name, {
|
|
468
|
+
source,
|
|
469
|
+
enabled: true,
|
|
470
|
+
...(current?.instructionEntry === undefined
|
|
471
|
+
? {}
|
|
472
|
+
: { instructionEntry: current.instructionEntry }),
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
const restoreLockedPackage = (name, entry) => Effect.gen(function* () {
|
|
476
|
+
const ref = yield* knowledgeLockEntryToRef(name, entry, {
|
|
477
|
+
baseDir,
|
|
478
|
+
path,
|
|
479
|
+
scope: ws.scope,
|
|
480
|
+
getConfiguredSourceByName: (sourceName) => ws.getConfiguredSourceByName(sourceName),
|
|
481
|
+
});
|
|
482
|
+
if (ref.refType === "registry" && Option.isNone(ref.integrity)) {
|
|
483
|
+
return yield* new KnowledgeUnavailable({
|
|
484
|
+
detail: `Locked registry Knowledge bundle has no integrity and cannot be restored: ${name}`,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
if (ref.refType !== "git-hosted") {
|
|
488
|
+
return yield* preparePackage(ref);
|
|
489
|
+
}
|
|
490
|
+
const discovered = yield* sources.find(ref.source, {
|
|
491
|
+
names: [name],
|
|
492
|
+
type: "knowledge",
|
|
493
|
+
owner: Option.none(),
|
|
494
|
+
versionRange: Option.none(),
|
|
495
|
+
});
|
|
496
|
+
const candidate = discovered.find((item) => item.type === "knowledge" && item.refType === "git-hosted");
|
|
497
|
+
if (candidate === undefined ||
|
|
498
|
+
candidate.gitTreeSha !== ref.gitTreeSha ||
|
|
499
|
+
candidate.gitCommitSha !== ref.gitCommitSha) {
|
|
500
|
+
return yield* new KnowledgeUnavailable({
|
|
501
|
+
detail: `Git Knowledge source no longer resolves to locked commit/tree ${ref.gitCommitSha}/${ref.gitTreeSha}: ${name}`,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
return yield* preparePackage(candidate);
|
|
505
|
+
});
|
|
506
|
+
const syncLocked = (dryRun) => Effect.gen(function* () {
|
|
507
|
+
const desired = yield* activeKnowledgeNodes();
|
|
508
|
+
const locked = yield* getKnowledgeLockEntries(ws);
|
|
509
|
+
const prepared = [];
|
|
510
|
+
for (const node of desired) {
|
|
511
|
+
const { name } = node;
|
|
512
|
+
const entry = locked[name];
|
|
513
|
+
const root = yield* desiredCanonicalRoot(node, entry);
|
|
514
|
+
const inspection = yield* Effect.result(inspectPackage(root));
|
|
515
|
+
if (Result.isSuccess(inspection))
|
|
516
|
+
continue;
|
|
517
|
+
if (node.identity.startsWith("workspace:")) {
|
|
518
|
+
return yield* new KnowledgeDefinitionInvalid({
|
|
519
|
+
detail: `Active workspace-authored Knowledge bundle is missing or invalid: ${name}`,
|
|
520
|
+
cause: inspection.failure,
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
if (entry === undefined) {
|
|
524
|
+
return yield* new KnowledgeResolutionMissing({ name });
|
|
525
|
+
}
|
|
526
|
+
const restored = yield* Effect.result(restoreLockedPackage(name, entry));
|
|
527
|
+
if (Result.isFailure(restored)) {
|
|
528
|
+
yield* Effect.forEach([...prepared].reverse(), (item) => item.rollback, {
|
|
529
|
+
discard: true,
|
|
530
|
+
});
|
|
531
|
+
const restoreFailure = restored.failure;
|
|
532
|
+
return yield* new KnowledgeUnavailable({
|
|
533
|
+
detail: `Active Knowledge bundle could not be restored: ${name}. ${describeKnowledgeFailure(restoreFailure)}`,
|
|
534
|
+
cause: restoreFailure,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
else
|
|
538
|
+
prepared.push(restored.success);
|
|
539
|
+
}
|
|
540
|
+
const discovered = yield* Effect.result(reconcileDiscovery({ dryRun: true }));
|
|
541
|
+
if (Result.isFailure(discovered)) {
|
|
542
|
+
yield* Effect.forEach([...prepared].reverse(), (item) => item.rollback, {
|
|
543
|
+
discard: true,
|
|
544
|
+
});
|
|
545
|
+
return yield* discovered.failure;
|
|
546
|
+
}
|
|
547
|
+
if (!dryRun)
|
|
548
|
+
yield* applyKnowledgeProjection;
|
|
549
|
+
yield* Effect.forEach(dryRun ? [...prepared].reverse() : prepared, (item) => (dryRun ? item.rollback : item.commit), { discard: true });
|
|
550
|
+
const discovery = discovered.success;
|
|
551
|
+
return {
|
|
552
|
+
changed: prepared.length > 0 || discovery.changed,
|
|
553
|
+
warnings: discovery.warnings,
|
|
554
|
+
artifacts: discovery.artifacts,
|
|
555
|
+
};
|
|
556
|
+
});
|
|
557
|
+
const installAtomically = (args) => runWorkspaceTransaction({
|
|
558
|
+
transition: Effect.gen(function* () {
|
|
559
|
+
const name = args.ref.knowledge.name;
|
|
560
|
+
const relativeLocalSource = args.ref.refType === "local"
|
|
561
|
+
? makeWorkspaceRelativeSourcePath(path, baseDir, args.ref.sourcePath ?? stripFileProtocol(args.ref.location))
|
|
562
|
+
: Option.none();
|
|
563
|
+
if (args.ref.refType === "local" && Option.isNone(relativeLocalSource)) {
|
|
564
|
+
return yield* new KnowledgeDefinitionInvalid({
|
|
565
|
+
detail: `Local knowledge source must stay within the workspace: ${args.ref.source.path}`,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
const cleanupSupersededCanonical = yield* provide(prepareAcceptedCanonicalTransition({
|
|
569
|
+
workspace: ws,
|
|
570
|
+
type: "knowledge",
|
|
571
|
+
name,
|
|
572
|
+
ref: args.ref,
|
|
573
|
+
}));
|
|
574
|
+
const prepared = yield* preparePackage(args.ref);
|
|
575
|
+
const committed = yield* Effect.gen(function* () {
|
|
576
|
+
const facts = acquiredFacts(prepared, relativeLocalSource);
|
|
577
|
+
if (args.ref.refType !== "workspace" && prepared.treeIntegrity === undefined) {
|
|
578
|
+
return yield* new KnowledgeInstallStateMissing({
|
|
579
|
+
name,
|
|
580
|
+
kind: "staged-tree-integrity",
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
const lockEntry = yield* buildLockEntry(args.ref, Option.some(facts));
|
|
584
|
+
if (Option.isSome(lockEntry)) {
|
|
585
|
+
yield* ws.setKnowledge({
|
|
586
|
+
name,
|
|
587
|
+
lockEntry: lockEntry.value,
|
|
588
|
+
versionRange: args.versionRange,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
yield* setKnowledgeSourceEntry(name, "workspace");
|
|
593
|
+
}
|
|
594
|
+
yield* cleanupSupersededCanonical;
|
|
595
|
+
return { name };
|
|
596
|
+
});
|
|
597
|
+
if (args.deferProjection !== true)
|
|
598
|
+
yield* applyKnowledgeProjection;
|
|
599
|
+
// The workspace transaction owns a nested scope. Settle the staged
|
|
600
|
+
// package before that scope closes; a later postcondition failure
|
|
601
|
+
// still restores the transaction snapshot.
|
|
602
|
+
yield* prepared.commit;
|
|
603
|
+
return committed;
|
|
604
|
+
}),
|
|
605
|
+
validate: ({ name }) => isObservedInstalled(ws, "knowledge", name).pipe(Effect.flatMap((installed) => installed ? Effect.void : new KnowledgeObservableContractViolated({ name }))),
|
|
606
|
+
}).pipe(Effect.scoped, Effect.asVoid);
|
|
607
|
+
// Canonical removal only. The shared operation flow re-renders the
|
|
608
|
+
// discovery region after settings and lock removal, once the target has
|
|
609
|
+
// left the graph.
|
|
610
|
+
const materializeUninstall = Effect.fn("KnowledgeManager.materializeUninstall")(function* ({ target, }) {
|
|
611
|
+
const canonical = yield* provide(acceptedCanonicalObservation({
|
|
612
|
+
workspace: ws,
|
|
613
|
+
type: "knowledge",
|
|
614
|
+
name: target.name,
|
|
615
|
+
}));
|
|
616
|
+
const root = removableAcceptedCanonicalPath(canonical);
|
|
617
|
+
const locked = yield* ws.getLockedKnowledgeEntry(target.name);
|
|
618
|
+
const ownedRoot = Option.orElse(root, () => Option.map(locked, (entry) => canonicalRoot(target.name, entry)));
|
|
619
|
+
if (Option.isSome(ownedRoot)) {
|
|
620
|
+
yield* protectWorkspacePath(ownedRoot.value);
|
|
621
|
+
yield* fs.remove(ownedRoot.value, { recursive: true, force: true }).pipe(Effect.mapError((cause) => new KnowledgeIoFailed({
|
|
622
|
+
detail: `Failed to remove Knowledge package source: ${ownedRoot.value}`,
|
|
623
|
+
cause,
|
|
624
|
+
})));
|
|
625
|
+
}
|
|
626
|
+
return withdrawn;
|
|
627
|
+
});
|
|
628
|
+
// Deactivation retains canonical content; the caller updates settings
|
|
629
|
+
// first, so re-rendering the whole region drops this bundle's routing.
|
|
630
|
+
const materializeDeactivate = Effect.fn("KnowledgeManager.materializeDeactivate")(() => applyKnowledgeProjection.pipe(Effect.as(withdrawn)));
|
|
631
|
+
return {
|
|
632
|
+
type: "knowledge",
|
|
633
|
+
projectionPlans,
|
|
634
|
+
refreshCatalog: () => runWorkspaceTransaction({
|
|
635
|
+
transition: applyKnowledgeProjection,
|
|
636
|
+
validate: () => Effect.void,
|
|
637
|
+
}),
|
|
638
|
+
sync: ({ dryRun }) => dryRun
|
|
639
|
+
? Effect.scoped(syncLocked(true))
|
|
640
|
+
: Effect.scoped(runWorkspaceTransaction({
|
|
641
|
+
transition: syncLocked(false),
|
|
642
|
+
validate: () => Effect.void,
|
|
643
|
+
})),
|
|
644
|
+
install: installAtomically,
|
|
645
|
+
isInstalled: ({ target }) => isObservedInstalled(ws, "knowledge", target.name),
|
|
646
|
+
materializeInstall: Effect.fn("KnowledgeManager.materializeInstall")(function* ({ ref, force, }) {
|
|
647
|
+
const relativeLocalSource = ref.refType === "local"
|
|
648
|
+
? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
|
|
649
|
+
: Option.none();
|
|
650
|
+
if (ref.refType === "local" && Option.isNone(relativeLocalSource)) {
|
|
651
|
+
return yield* new KnowledgeDefinitionInvalid({
|
|
652
|
+
detail: `Local knowledge source must stay within the workspace: ${ref.source.path}`,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
const prepared = yield* preparePackage(ref, force === true);
|
|
656
|
+
yield* prepared.commit;
|
|
657
|
+
return acquiredFacts(prepared, relativeLocalSource);
|
|
658
|
+
}, Effect.scoped),
|
|
659
|
+
prepareSourceTransition: ({ ref }) => provide(prepareAcceptedCanonicalTransition({
|
|
660
|
+
workspace: ws,
|
|
661
|
+
type: "knowledge",
|
|
662
|
+
name: ref.knowledge.name,
|
|
663
|
+
ref,
|
|
664
|
+
})),
|
|
665
|
+
getConfiguredSource: ({ target }) => ws
|
|
666
|
+
.getConfiguredKnowledgeEntries()
|
|
667
|
+
.pipe(Effect.map((entries) => Option.fromUndefinedOr(entries[target.name]?.source))),
|
|
668
|
+
/**
|
|
669
|
+
* Every enabled entry's accepted canonical package, read from accepted
|
|
670
|
+
* resolution rather than re-resolved from source. Materialization
|
|
671
|
+
* realizes what the workspace already accepted; going back to the
|
|
672
|
+
* source would put an unrelated configured entry's release age between
|
|
673
|
+
* an operator and the extension they are authoring.
|
|
674
|
+
*/
|
|
675
|
+
listMaterializable: () => Effect.gen(function* () {
|
|
676
|
+
const nodes = yield* activeKnowledgeNodes();
|
|
677
|
+
const refs = yield* Effect.forEach(nodes, (node) => provide(usableAcceptedCanonicalRef({
|
|
678
|
+
workspace: ws,
|
|
679
|
+
type: "knowledge",
|
|
680
|
+
name: node.name,
|
|
681
|
+
}).pipe(Effect.map(Option.filter((ref) => ref.type === "knowledge")))), { concurrency: "unbounded" });
|
|
682
|
+
return refs.flatMap((ref) => (Option.isSome(ref) ? [ref.value] : []));
|
|
683
|
+
}),
|
|
684
|
+
materializeUninstall,
|
|
685
|
+
materializeDeactivate,
|
|
686
|
+
upsertSettingsEntry: ({ ref, versionRange, materialization }) => buildLockEntry(ref, materialization).pipe(Effect.flatMap((lockEntry) => Option.isSome(lockEntry)
|
|
687
|
+
? ws.setKnowledge({
|
|
688
|
+
name: ref.knowledge.name,
|
|
689
|
+
lockEntry: lockEntry.value,
|
|
690
|
+
versionRange,
|
|
691
|
+
})
|
|
692
|
+
: setKnowledgeSourceEntry(ref.knowledge.name, "workspace"))),
|
|
693
|
+
removeSettingsEntry: ({ target }) => ws.removeKnowledgeSettings(target.name),
|
|
694
|
+
upsertLockfileEntry: ({ ref, materialization }) => buildLockEntry(ref, materialization).pipe(Effect.flatMap((lockEntry) => {
|
|
695
|
+
if (Option.isNone(lockEntry))
|
|
696
|
+
return ws.removeKnowledgeLock(ref.knowledge.name);
|
|
697
|
+
const validate = lockEntry.value.type === "registry"
|
|
698
|
+
? validateExactResolvedVersion(`knowledge.${ref.knowledge.name}.resolvedVersion`, lockEntry.value.resolvedVersion)
|
|
699
|
+
: Effect.void;
|
|
700
|
+
return validate.pipe(Effect.flatMap(() => ws.setKnowledgeLock({
|
|
701
|
+
name: ref.knowledge.name,
|
|
702
|
+
lockEntry: lockEntry.value,
|
|
703
|
+
versionRange: Option.none(),
|
|
704
|
+
})));
|
|
705
|
+
})),
|
|
706
|
+
removeLockfileEntry: ({ target }) => ws.removeKnowledgeLock(target.name),
|
|
707
|
+
};
|
|
708
|
+
}));
|
|
709
|
+
//# sourceMappingURL=manager.js.map
|