@agentxm/extension-authoring 0.30.1 → 0.30.2
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/adopt/adopt-extension.d.ts +19 -7
- package/dist/src/adopt/adopt-extension.js +105 -36
- package/package.json +15 -15
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* was never declared becomes enabled — adopting content you cannot see would
|
|
9
9
|
* leave the workspace holding authored work it never materializes.
|
|
10
10
|
*
|
|
11
|
+
* A package already at its authoring location that nothing declares is
|
|
12
|
+
* adopted in place: the workspace declares it, enabled, and nothing moves.
|
|
13
|
+
* In-place adoption refuses when an installed copy of the same identity also
|
|
14
|
+
* exists, when the workspace already declares the name, and when the authored
|
|
15
|
+
* manifest is invalid or names a different identity.
|
|
16
|
+
*
|
|
11
17
|
* `prepare` settles the identity, the destination, and the activation, and
|
|
12
18
|
* writes nothing. `previewOrApply` resolves that same candidate inside the
|
|
13
19
|
* workspace transaction, so a failed move restores both directories.
|
|
@@ -17,14 +23,15 @@
|
|
|
17
23
|
import * as Effect from "effect/Effect";
|
|
18
24
|
import * as FileSystem from "effect/FileSystem";
|
|
19
25
|
import type * as HttpClient from "effect/unstable/http/HttpClient";
|
|
26
|
+
import * as Option from "effect/Option";
|
|
20
27
|
import * as Path from "effect/Path";
|
|
21
28
|
import { ExtensionManagers, McpSecretStore, type ManagerRequirements } from "@agentxm/extension-materialization";
|
|
22
29
|
import { type RecipeRequirements } from "@agentxm/workspace-reconciliation";
|
|
23
30
|
import { type ExtensionType, type FqnInvalidError, type Handle } from "@agentxm/extension-model/unstable/extensions";
|
|
24
31
|
import { type CandidateFingerprintFailed, type ExecutionCandidate, type PlanExecution } from "@agentxm/workspace-operations";
|
|
25
32
|
import type { CodingAgentRepository } from "@agentxm/workspace-projection";
|
|
26
|
-
import { WorkspaceMutations, type ConfiguredAgentOutcomesProvider, type LockfileValidationError, type WorkspaceLockfileReadFailure, type WorkspaceSettingsReadFailure } from "@agentxm/workspace-state";
|
|
27
|
-
import type
|
|
33
|
+
import { LockfileReader, WorkspaceMutations, type ConfiguredAgentOutcomesProvider, type LockfileValidationError, type WorkspaceLockfileReadFailure, type WorkspaceSettingsReadFailure, type WorkspaceStateReadFailure } from "@agentxm/workspace-state";
|
|
34
|
+
import { type AuthoredPackageError } from "../authored-package-errors.js";
|
|
28
35
|
import { AuthoringFailed } from "../errors.js";
|
|
29
36
|
import { AuthoringScopeUnsupported, type AuthoringOwnerMismatch, type AuthoringOwnerRequired } from "../create/errors.js";
|
|
30
37
|
export interface AdoptExtensionRequest {
|
|
@@ -44,9 +51,14 @@ export interface AdoptExtensionCandidate {
|
|
|
44
51
|
readonly fqn: string;
|
|
45
52
|
readonly owner: Handle;
|
|
46
53
|
readonly name: string;
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
/**
|
|
55
|
+
* `move` relocates an installed copy into authorship; `in-place` declares a
|
|
56
|
+
* package already at its authoring location and moves nothing.
|
|
57
|
+
*/
|
|
58
|
+
readonly mode: "move" | "in-place";
|
|
59
|
+
/** Workspace-relative directory the package is moved out of, when it moves. */
|
|
60
|
+
readonly acquiredPath: Option.Option<string>;
|
|
61
|
+
/** Workspace-relative authoring directory the adopted package occupies. */
|
|
50
62
|
readonly authoredPath: string;
|
|
51
63
|
/** Workspace-relative settings file the declaration is rewritten in. */
|
|
52
64
|
readonly settingsPath: string;
|
|
@@ -55,9 +67,9 @@ export interface AdoptExtensionCandidate {
|
|
|
55
67
|
readonly execution: ExecutionCandidate<AdoptExtensionRequirements>;
|
|
56
68
|
}
|
|
57
69
|
/** Every failure settling an adoption can surface before anything is written. */
|
|
58
|
-
export type AdoptExtensionFailure = AuthoringFailed | AuthoredPackageError | AuthoringOwnerRequired | AuthoringOwnerMismatch | AuthoringScopeUnsupported | LockfileValidationError | WorkspaceLockfileReadFailure | WorkspaceSettingsReadFailure | CandidateFingerprintFailed | FqnInvalidError;
|
|
70
|
+
export type AdoptExtensionFailure = AuthoringFailed | AuthoredPackageError | AuthoringOwnerRequired | AuthoringOwnerMismatch | AuthoringScopeUnsupported | LockfileValidationError | WorkspaceLockfileReadFailure | WorkspaceSettingsReadFailure | WorkspaceStateReadFailure | CandidateFingerprintFailed | FqnInvalidError;
|
|
59
71
|
/** Everything settling an adoption reads before it freezes a candidate. */
|
|
60
|
-
export type PrepareAdoptExtensionRequirements = FileSystem.FileSystem | Path.Path | HttpClient.HttpClient | WorkspaceMutations | ExtensionManagers | ConfiguredAgentOutcomesProvider;
|
|
72
|
+
export type PrepareAdoptExtensionRequirements = FileSystem.FileSystem | Path.Path | HttpClient.HttpClient | WorkspaceMutations | LockfileReader | ExtensionManagers | ConfiguredAgentOutcomesProvider;
|
|
61
73
|
/** The plan an adoption resolves. */
|
|
62
74
|
export declare const adoptExtensionPlanName = "Adopt workspace extension";
|
|
63
75
|
/** Settle an adoption without writing anything. */
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* was never declared becomes enabled — adopting content you cannot see would
|
|
9
9
|
* leave the workspace holding authored work it never materializes.
|
|
10
10
|
*
|
|
11
|
+
* A package already at its authoring location that nothing declares is
|
|
12
|
+
* adopted in place: the workspace declares it, enabled, and nothing moves.
|
|
13
|
+
* In-place adoption refuses when an installed copy of the same identity also
|
|
14
|
+
* exists, when the workspace already declares the name, and when the authored
|
|
15
|
+
* manifest is invalid or names a different identity.
|
|
16
|
+
*
|
|
11
17
|
* `prepare` settles the identity, the destination, and the activation, and
|
|
12
18
|
* writes nothing. `previewOrApply` resolves that same candidate inside the
|
|
13
19
|
* workspace transaction, so a failed move restores both directories.
|
|
@@ -23,9 +29,11 @@ import { materializeAuthoredMcpServer } from "@agentxm/workspace-reconciliation"
|
|
|
23
29
|
import { buildAuthoredExtensionStep, } from "@agentxm/workspace-reconciliation";
|
|
24
30
|
import { extensionTypeToPlural, formatFqn, parseFqn, } from "@agentxm/extension-model/unstable/extensions";
|
|
25
31
|
import { operationPresentation, prepareExecutionCandidate, resolveExecutionCandidate, } from "@agentxm/workspace-operations";
|
|
26
|
-
import { WorkspaceMutations, resolveWorkspaceExtensionRef, } from "@agentxm/workspace-state";
|
|
32
|
+
import { LockfileReader, WorkspaceMutations, observeInstallRoot, resolveWorkspaceExtensionRef, } from "@agentxm/workspace-state";
|
|
27
33
|
import { protectCreatedAncestors } from "@agentxm/workspace-transactions";
|
|
34
|
+
import { CreateDestinationExists } from "@agentxm/extension-materialization";
|
|
28
35
|
import { authoredDeclaration } from "../authored-declaration.js";
|
|
36
|
+
import { CreateDestinationInspectionFailed, } from "../authored-package-errors.js";
|
|
29
37
|
import { preflightCreateOnly } from "../create-preflight.js";
|
|
30
38
|
import { AuthoringFailed } from "../errors.js";
|
|
31
39
|
import { requireAuthoredOwner, settingsRelativePath } from "../create/authoring-owner.js";
|
|
@@ -46,6 +54,7 @@ export const prepareAdoptExtension = Effect.fn("AdoptExtension.prepare")(functio
|
|
|
46
54
|
const fs = yield* FileSystem.FileSystem;
|
|
47
55
|
const path = yield* Path.Path;
|
|
48
56
|
const managers = yield* ExtensionManagers;
|
|
57
|
+
const locks = yield* LockfileReader;
|
|
49
58
|
const parsed = yield* Effect.fromResult(parseFqn(request.fqn));
|
|
50
59
|
if (ws.layout.scope !== "project") {
|
|
51
60
|
return yield* new AuthoringScopeUnsupported({ subject: "adoption", scope: ws.layout.scope });
|
|
@@ -58,6 +67,8 @@ export const prepareAdoptExtension = Effect.fn("AdoptExtension.prepare")(functio
|
|
|
58
67
|
const acquiredPath = path.relative(ws.baseDir, sourceDir);
|
|
59
68
|
const authoredPath = path.relative(ws.baseDir, targetDir);
|
|
60
69
|
const settingsPath = settingsRelativePath(path, ws);
|
|
70
|
+
const declaration = authoredDeclaration(ws, parsed.type, name);
|
|
71
|
+
const current = yield* declaration.read;
|
|
61
72
|
// Refuse an occupied authoring destination here, so a preview refuses it
|
|
62
73
|
// too; the closure repeats the check under the transaction lock.
|
|
63
74
|
const createOnly = preflightCreateOnly({
|
|
@@ -66,13 +77,50 @@ export const prepareAdoptExtension = Effect.fn("AdoptExtension.prepare")(functio
|
|
|
66
77
|
configured: false,
|
|
67
78
|
destinations: [targetDir],
|
|
68
79
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
80
|
+
// In-place adoption is decided from what is on disk now and repeated under
|
|
81
|
+
// the transaction lock, so preview and apply refuse the same states.
|
|
82
|
+
const inPlaceRefusals = Effect.gen(function* () {
|
|
83
|
+
const inventory = yield* observeInstallRoot({
|
|
84
|
+
layout: ws.layout,
|
|
85
|
+
graph: yield* ws.getDesiredStateGraph(),
|
|
86
|
+
locks,
|
|
87
|
+
});
|
|
88
|
+
const installedCopy = inventory.packages.find((entry) => entry.type === parsed.type &&
|
|
89
|
+
entry.name === name &&
|
|
90
|
+
(entry.owner === undefined || entry.owner === parsed.owner));
|
|
91
|
+
// Two copies of one identity leave adoption no single content to author.
|
|
92
|
+
if (installedCopy !== undefined) {
|
|
93
|
+
return yield* new CreateDestinationExists({ subject: "Adopt target", path: targetDir });
|
|
94
|
+
}
|
|
95
|
+
const declared = yield* declaration.read;
|
|
96
|
+
if (declared.configured) {
|
|
97
|
+
return yield* new AuthoringFailed({
|
|
98
|
+
category: "conflict",
|
|
99
|
+
detail: `${fqn} is already declared in ${settingsPath}`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
yield* resolveWorkspaceExtensionRef({
|
|
103
|
+
settingsName: name,
|
|
104
|
+
source: "workspace",
|
|
105
|
+
expectedType: parsed.type,
|
|
106
|
+
layout: ws.layout,
|
|
107
|
+
scope: ws.scope,
|
|
108
|
+
staticPackage: { owner: parsed.owner, name, root: targetDir },
|
|
109
|
+
}).pipe(Effect.mapError((cause) => new AuthoringFailed({
|
|
110
|
+
category: "validation",
|
|
111
|
+
detail: `The authored package at ${authoredPath} is not a valid ${fqn} package`,
|
|
112
|
+
cause,
|
|
113
|
+
})));
|
|
114
|
+
});
|
|
115
|
+
const authoredExists = yield* fs
|
|
116
|
+
.exists(targetDir)
|
|
117
|
+
.pipe(Effect.mapError((cause) => new CreateDestinationInspectionFailed({ path: targetDir, cause })));
|
|
118
|
+
const mode = authoredExists ? "in-place" : "move";
|
|
119
|
+
yield* mode === "in-place" ? inPlaceRefusals : createOnly;
|
|
72
120
|
// A package the workspace never declared becomes enabled: adopting content
|
|
73
121
|
// that stays invisible is not what adoption was asked for.
|
|
74
|
-
const enabled = Option.getOrElse(current.enabled, () => true);
|
|
75
|
-
const
|
|
122
|
+
const enabled = mode === "in-place" ? true : Option.getOrElse(current.enabled, () => true);
|
|
123
|
+
const moveArtifact = {
|
|
76
124
|
path: authoredPath,
|
|
77
125
|
scope: ws.scope,
|
|
78
126
|
change: "created",
|
|
@@ -82,43 +130,63 @@ export const prepareAdoptExtension = Effect.fn("AdoptExtension.prepare")(functio
|
|
|
82
130
|
{ path: settingsPath, change: "updated" },
|
|
83
131
|
],
|
|
84
132
|
};
|
|
85
|
-
|
|
133
|
+
// In-place adoption writes only the declaration; the authored package keeps
|
|
134
|
+
// every byte, so it is not reported as a changed path.
|
|
135
|
+
const inPlaceArtifact = {
|
|
136
|
+
path: settingsPath,
|
|
137
|
+
scope: ws.scope,
|
|
138
|
+
change: "updated",
|
|
139
|
+
targets: [{ path: settingsPath, change: "updated" }],
|
|
140
|
+
};
|
|
141
|
+
const shared = {
|
|
86
142
|
toStepFailure: authoringStepFailure,
|
|
87
143
|
location: targetDir,
|
|
88
|
-
transactionTargets: [sourceDir],
|
|
89
144
|
versionRange: Option.none(),
|
|
90
145
|
label: `Adopt ${fqn}`,
|
|
91
|
-
message: `Adopted ${fqn}`,
|
|
92
146
|
enabled,
|
|
93
|
-
allowConfiguredSourceTransition: true,
|
|
94
147
|
markAuthored: Effect.andThen(declaration.retireExternalResolution, declaration.declare({ enabled: true, env: current.env })),
|
|
95
148
|
finalizeAuthored: declaration.declare({ enabled, env: current.env }),
|
|
96
|
-
plannedArtifact: artifact,
|
|
97
|
-
buildArtifact: () => Effect.succeed(artifact),
|
|
98
|
-
preflight: Effect.gen(function* () {
|
|
99
|
-
yield* createOnly;
|
|
100
|
-
// The acquired directory must already hold a resolvable package: a move
|
|
101
|
-
// that lands unreadable content would leave the workspace authoring
|
|
102
|
-
// something it cannot materialize.
|
|
103
|
-
yield* resolveWorkspaceExtensionRef({
|
|
104
|
-
settingsName: name,
|
|
105
|
-
source: "workspace",
|
|
106
|
-
expectedType: parsed.type,
|
|
107
|
-
layout: ws.layout,
|
|
108
|
-
scope: ws.scope,
|
|
109
|
-
staticPackage: { owner: parsed.owner, name, root: sourceDir },
|
|
110
|
-
});
|
|
111
|
-
}).pipe(Effect.asVoid),
|
|
112
|
-
scaffold: Effect.gen(function* () {
|
|
113
|
-
yield* protectCreatedAncestors(fs, path, path.dirname(targetDir));
|
|
114
|
-
yield* fs.makeDirectory(path.dirname(targetDir), { recursive: true });
|
|
115
|
-
yield* fs.rename(sourceDir, targetDir);
|
|
116
|
-
}).pipe(Effect.mapError((cause) => new AuthoringFailed({
|
|
117
|
-
category: "internal",
|
|
118
|
-
detail: `Could not move ${fqn} into authored package storage`,
|
|
119
|
-
cause,
|
|
120
|
-
}))),
|
|
121
149
|
};
|
|
150
|
+
const common = mode === "in-place"
|
|
151
|
+
? {
|
|
152
|
+
...shared,
|
|
153
|
+
message: `Adopted ${fqn} in place`,
|
|
154
|
+
plannedArtifact: inPlaceArtifact,
|
|
155
|
+
buildArtifact: () => Effect.succeed(inPlaceArtifact),
|
|
156
|
+
preflight: inPlaceRefusals,
|
|
157
|
+
scaffold: Effect.void,
|
|
158
|
+
}
|
|
159
|
+
: {
|
|
160
|
+
...shared,
|
|
161
|
+
message: `Adopted ${fqn}`,
|
|
162
|
+
transactionTargets: [sourceDir],
|
|
163
|
+
allowConfiguredSourceTransition: true,
|
|
164
|
+
plannedArtifact: moveArtifact,
|
|
165
|
+
buildArtifact: () => Effect.succeed(moveArtifact),
|
|
166
|
+
preflight: Effect.gen(function* () {
|
|
167
|
+
yield* createOnly;
|
|
168
|
+
// The acquired directory must already hold a resolvable package: a move
|
|
169
|
+
// that lands unreadable content would leave the workspace authoring
|
|
170
|
+
// something it cannot materialize.
|
|
171
|
+
yield* resolveWorkspaceExtensionRef({
|
|
172
|
+
settingsName: name,
|
|
173
|
+
source: "workspace",
|
|
174
|
+
expectedType: parsed.type,
|
|
175
|
+
layout: ws.layout,
|
|
176
|
+
scope: ws.scope,
|
|
177
|
+
staticPackage: { owner: parsed.owner, name, root: sourceDir },
|
|
178
|
+
});
|
|
179
|
+
}).pipe(Effect.asVoid),
|
|
180
|
+
scaffold: Effect.gen(function* () {
|
|
181
|
+
yield* protectCreatedAncestors(fs, path, path.dirname(targetDir));
|
|
182
|
+
yield* fs.makeDirectory(path.dirname(targetDir), { recursive: true });
|
|
183
|
+
yield* fs.rename(sourceDir, targetDir);
|
|
184
|
+
}).pipe(Effect.mapError((cause) => new AuthoringFailed({
|
|
185
|
+
category: "internal",
|
|
186
|
+
detail: `Could not move ${fqn} into authored package storage`,
|
|
187
|
+
cause,
|
|
188
|
+
}))),
|
|
189
|
+
};
|
|
122
190
|
const step = (() => {
|
|
123
191
|
switch (parsed.type) {
|
|
124
192
|
case "skill":
|
|
@@ -156,7 +224,8 @@ export const prepareAdoptExtension = Effect.fn("AdoptExtension.prepare")(functio
|
|
|
156
224
|
fqn,
|
|
157
225
|
owner: parsed.owner,
|
|
158
226
|
name,
|
|
159
|
-
|
|
227
|
+
mode,
|
|
228
|
+
acquiredPath: mode === "move" ? Option.some(acquiredPath) : Option.none(),
|
|
160
229
|
authoredPath,
|
|
161
230
|
settingsPath,
|
|
162
231
|
enabled,
|
package/package.json
CHANGED
|
@@ -4,26 +4,26 @@
|
|
|
4
4
|
"url": "https://github.com/agentxm/axm/issues"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@agentxm/extension-content": "^0.30.
|
|
8
|
-
"@agentxm/extension-materialization": "^0.30.
|
|
9
|
-
"@agentxm/extension-model": "^0.30.
|
|
10
|
-
"@agentxm/extension-sources": "^0.30.
|
|
11
|
-
"@agentxm/registry-auth": "^0.30.
|
|
12
|
-
"@agentxm/registry-client": "^0.30.
|
|
13
|
-
"@agentxm/workspace-operations": "^0.30.
|
|
14
|
-
"@agentxm/workspace-projection": "^0.30.
|
|
15
|
-
"@agentxm/workspace-reconciliation": "^0.30.
|
|
16
|
-
"@agentxm/workspace-state": "^0.30.
|
|
17
|
-
"@agentxm/workspace-transactions": "^0.30.
|
|
7
|
+
"@agentxm/extension-content": "^0.30.2",
|
|
8
|
+
"@agentxm/extension-materialization": "^0.30.2",
|
|
9
|
+
"@agentxm/extension-model": "^0.30.2",
|
|
10
|
+
"@agentxm/extension-sources": "^0.30.2",
|
|
11
|
+
"@agentxm/registry-auth": "^0.30.2",
|
|
12
|
+
"@agentxm/registry-client": "^0.30.2",
|
|
13
|
+
"@agentxm/workspace-operations": "^0.30.2",
|
|
14
|
+
"@agentxm/workspace-projection": "^0.30.2",
|
|
15
|
+
"@agentxm/workspace-reconciliation": "^0.30.2",
|
|
16
|
+
"@agentxm/workspace-state": "^0.30.2",
|
|
17
|
+
"@agentxm/workspace-transactions": "^0.30.2",
|
|
18
18
|
"effect": "4.0.0-rc.115",
|
|
19
19
|
"semver": "^7.8.5",
|
|
20
20
|
"yaml": "^2.9.0"
|
|
21
21
|
},
|
|
22
22
|
"description": "AXM extension-authoring feature: new-extension scaffolding, fork, native import, adopt/demote identity policy, and authored pack membership for the axm CLI. Unstable and unsupported — use the axm.sh CLI.",
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@agentxm/agent-integration": "^0.30.
|
|
25
|
-
"@agentxm/extension-resolution": "^0.30.
|
|
26
|
-
"@agentxm/specification-metadata": "^0.30.
|
|
24
|
+
"@agentxm/agent-integration": "^0.30.2",
|
|
25
|
+
"@agentxm/extension-resolution": "^0.30.2",
|
|
26
|
+
"@agentxm/specification-metadata": "^0.30.2",
|
|
27
27
|
"@effect/platform-node": "4.0.0-rc.115",
|
|
28
28
|
"@effect/vitest": "4.0.0-rc.115",
|
|
29
29
|
"@types/bun": "^1.3.14",
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
},
|
|
66
66
|
"sideEffects": false,
|
|
67
67
|
"type": "module",
|
|
68
|
-
"version": "0.30.
|
|
68
|
+
"version": "0.30.2"
|
|
69
69
|
}
|