@agentxm/workspace-state 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/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/lockfile/lockfile.d.ts +5 -5
- package/dist/src/lockfile/lockfile.js +2 -1
- package/dist/src/workspace/install-root.d.ts +69 -0
- package/dist/src/workspace/install-root.js +149 -0
- package/dist/src/workspace/read-model/extensions/inventory.d.ts +21 -4
- package/dist/src/workspace/read-model/extensions/inventory.js +35 -13
- package/dist/src/workspace/read-model/scanners/canonical-extensions.js +2 -1
- package/dist/src/workspace/read-model/service.d.ts +3 -0
- package/dist/src/workspace/read-model/service.js +1 -0
- package/dist/src/workspace/read-model-record-readers.js +49 -20
- package/dist/src/workspace/service-interface.d.ts +1 -1
- package/dist/src/workspace/test-stubs.d.ts +2 -0
- package/dist/src/workspace/test-stubs.js +4 -0
- package/package.json +7 -7
package/dist/src/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export { SETTINGS_KNOWN_KEYS } from "./settings/schema.js";
|
|
|
22
22
|
export { gitSourceLockFields } from "./lockfile/entry-fields.js";
|
|
23
23
|
export { LOCK_ENTRY_SCHEMA_BY_TYPE } from "./lockfile/schema.js";
|
|
24
24
|
export { ACQUIRED_EXTENSIONS_DIR, LOCK_FILENAME } from "./workspace/constants.js";
|
|
25
|
+
export { isInstallRootStagingName, observeInstallRoot, type InstallRootEntry, type InstallRootInventory, type InstalledPackageEntry, type ObserveInstallRootArgs, type UnrecognizedInstallRootEntry, } from "./workspace/install-root.js";
|
|
25
26
|
export { acquiredExtensionDisplayPath, acquiredExtensionDisplayPathFromLockEntry, computeExtensionPathsForLayout, extensionPathSourceFromLockEntry, extensionContentFilename, extensionContentPath, type ExtensionPathLockEntry, type ExtensionPathSource, type ExtensionDirPaths, } from "./workspace/extension-paths.js";
|
|
26
27
|
export { RenderedFilePathSchema, RenderedFilesMapSchema, computeSourceHash, type RenderedFilePath, type RenderedFilesMap, } from "./workspace/rendered-files.js";
|
|
27
28
|
export { computePackageContentHash } from "./workspace/package-hash.js";
|
package/dist/src/index.js
CHANGED
|
@@ -27,6 +27,7 @@ export { gitSourceLockFields } from "./lockfile/entry-fields.js";
|
|
|
27
27
|
export { LOCK_ENTRY_SCHEMA_BY_TYPE } from "./lockfile/schema.js";
|
|
28
28
|
// Extension path and identity vocabulary
|
|
29
29
|
export { ACQUIRED_EXTENSIONS_DIR, LOCK_FILENAME } from "./workspace/constants.js";
|
|
30
|
+
export { isInstallRootStagingName, observeInstallRoot, } from "./workspace/install-root.js";
|
|
30
31
|
export { acquiredExtensionDisplayPath, acquiredExtensionDisplayPathFromLockEntry, computeExtensionPathsForLayout, extensionPathSourceFromLockEntry, extensionContentFilename, extensionContentPath, } from "./workspace/extension-paths.js";
|
|
31
32
|
export { RenderedFilePathSchema, RenderedFilesMapSchema, computeSourceHash, } from "./workspace/rendered-files.js";
|
|
32
33
|
export { computePackageContentHash } from "./workspace/package-hash.js";
|
|
@@ -38,8 +38,8 @@ export type LockfileUpdate = (lockfile: Lockfile) => Lockfile;
|
|
|
38
38
|
*
|
|
39
39
|
* @experimental This API is unstable and may change without notice.
|
|
40
40
|
*/
|
|
41
|
-
export declare const writeLockfileAtPath: (lockfilePath: string, lockfile: Lockfile) => Effect.Effect<void, LockfileWriteError, FileSystem.FileSystem | Path.Path>;
|
|
42
|
-
export declare const writeLockfile: (axmDir: string, lockfile: Lockfile) => Effect.Effect<void, LockfileWriteError, FileSystem.FileSystem | Path.Path>;
|
|
41
|
+
export declare const writeLockfileAtPath: (lockfilePath: string, lockfile: Lockfile) => Effect.Effect<void, import("@agentxm/workspace-transactions").WorkspaceSnapshotError | LockfileWriteError, FileSystem.FileSystem | Path.Path>;
|
|
42
|
+
export declare const writeLockfile: (axmDir: string, lockfile: Lockfile) => Effect.Effect<void, import("@agentxm/workspace-transactions").WorkspaceSnapshotError | LockfileWriteError, FileSystem.FileSystem | Path.Path>;
|
|
43
43
|
/**
|
|
44
44
|
* Applies lockfile updates in order without writing to disk.
|
|
45
45
|
*
|
|
@@ -782,7 +782,7 @@ export declare const commitLockfileUpdates: (axmDir: string, lockfile: Lockfile,
|
|
|
782
782
|
readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
|
|
783
783
|
};
|
|
784
784
|
} | undefined;
|
|
785
|
-
}, LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
785
|
+
}, import("@agentxm/workspace-transactions").WorkspaceSnapshotError | LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
786
786
|
/**
|
|
787
787
|
* Commits a caller-computed lockfile snapshot as an entry-level patch.
|
|
788
788
|
*
|
|
@@ -1522,7 +1522,7 @@ export declare const commitLockfileSnapshotUpdateAtPath: (lockfilePath: string,
|
|
|
1522
1522
|
readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
|
|
1523
1523
|
};
|
|
1524
1524
|
} | undefined;
|
|
1525
|
-
}, LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
1525
|
+
}, import("@agentxm/workspace-transactions").WorkspaceSnapshotError | LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
1526
1526
|
export declare const commitLockfileSnapshotUpdate: (axmDir: string, base: Lockfile, next: Lockfile) => Effect.Effect<{
|
|
1527
1527
|
readonly skills: {
|
|
1528
1528
|
readonly [x: string]: {
|
|
@@ -2251,5 +2251,5 @@ export declare const commitLockfileSnapshotUpdate: (axmDir: string, base: Lockfi
|
|
|
2251
2251
|
readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
|
|
2252
2252
|
};
|
|
2253
2253
|
} | undefined;
|
|
2254
|
-
}, LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
2254
|
+
}, import("@agentxm/workspace-transactions").WorkspaceSnapshotError | LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
2255
2255
|
//# sourceMappingURL=lockfile.d.ts.map
|
|
@@ -14,7 +14,7 @@ import * as Effect from "effect/Effect";
|
|
|
14
14
|
import * as Schema from "effect/Schema";
|
|
15
15
|
import * as Semaphore from "effect/Semaphore";
|
|
16
16
|
import YAML from "yaml";
|
|
17
|
-
import { recordFootprint, sweepStaleAtomicWriteTemps, writeFileAtomic, } from "@agentxm/workspace-transactions";
|
|
17
|
+
import { protectWorkspacePath, recordFootprint, sweepStaleAtomicWriteTemps, writeFileAtomic, } from "@agentxm/workspace-transactions";
|
|
18
18
|
import { LockfileValidationError, LockfileWriteError } from "./errors.js";
|
|
19
19
|
import { LOCKFILE_VERSION, LockfileSchema } from "./schema.js";
|
|
20
20
|
// -----------------------------------------------------------------------------
|
|
@@ -132,6 +132,7 @@ const withLockfileLock = (lockfilePath, effect) => Effect.gen(function* () {
|
|
|
132
132
|
const writeLockfileUnlocked = (lockfilePath, lockfile) => Effect.gen(function* () {
|
|
133
133
|
const fs = yield* FileSystem.FileSystem;
|
|
134
134
|
const yamlContent = yield* encodeLockfileYaml(lockfilePath, lockfile);
|
|
135
|
+
yield* protectWorkspacePath(lockfilePath);
|
|
135
136
|
const existed = yield* fs.exists(lockfilePath).pipe(Effect.orElseSucceed(() => true));
|
|
136
137
|
yield* sweepStaleAtomicWriteTemps(fs, lockfilePath);
|
|
137
138
|
const written = yield* writeFileAtomic(fs, {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install-root inventory.
|
|
3
|
+
*
|
|
4
|
+
* Classifies every entry in a scope's install root (`agent_extensions`): an
|
|
5
|
+
* installed package whose path gives a well-formed extension identity or is
|
|
6
|
+
* proven by an accepted resolution, AXM's own interrupted staging, or an
|
|
7
|
+
* unrecognized entry. Each installed package records whether desired state
|
|
8
|
+
* still reaches it; one that nothing reaches is a leftover.
|
|
9
|
+
*
|
|
10
|
+
* Location inside the install root is AXM's install proof for canonical
|
|
11
|
+
* package directories only. It never proves ownership of agent projections.
|
|
12
|
+
*
|
|
13
|
+
* @experimental This API is unstable and may change without notice.
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
import * as Effect from "effect/Effect";
|
|
17
|
+
import * as FileSystem from "effect/FileSystem";
|
|
18
|
+
import * as Path from "effect/Path";
|
|
19
|
+
import { type ExtensionType } from "@agentxm/extension-model/unstable/extensions";
|
|
20
|
+
import type { DesiredStateGraph } from "./desired-state-graph.js";
|
|
21
|
+
import type { WorkspaceLayout } from "./layout.js";
|
|
22
|
+
import type { LockfileReaderService } from "./lockfile-reader.js";
|
|
23
|
+
/** An installed package directory in the install root. */
|
|
24
|
+
export interface InstalledPackageEntry {
|
|
25
|
+
readonly kind: "package";
|
|
26
|
+
readonly type: ExtensionType;
|
|
27
|
+
readonly name: string;
|
|
28
|
+
/** The owner handle the path names, when the path gives one. */
|
|
29
|
+
readonly owner: string | undefined;
|
|
30
|
+
/** The source directory the package sits under, when the path gives one. */
|
|
31
|
+
readonly sourceDirectory: string | undefined;
|
|
32
|
+
/** Absolute canonical package path. */
|
|
33
|
+
readonly path: string;
|
|
34
|
+
/** The lock key of the accepted resolution whose canonical path this is. */
|
|
35
|
+
readonly lockKey: string | undefined;
|
|
36
|
+
/** Whether any desired route reaches this canonical package. */
|
|
37
|
+
readonly reached: boolean;
|
|
38
|
+
}
|
|
39
|
+
/** An install-root entry that is neither an installed package nor AXM staging. */
|
|
40
|
+
export interface UnrecognizedInstallRootEntry {
|
|
41
|
+
readonly kind: "unrecognized";
|
|
42
|
+
readonly path: string;
|
|
43
|
+
readonly entryKind: "file" | "directory" | "symlink";
|
|
44
|
+
}
|
|
45
|
+
export type InstallRootEntry = InstalledPackageEntry | UnrecognizedInstallRootEntry;
|
|
46
|
+
export interface InstallRootInventory {
|
|
47
|
+
readonly root: string;
|
|
48
|
+
readonly packages: ReadonlyArray<InstalledPackageEntry>;
|
|
49
|
+
readonly leftovers: ReadonlyArray<InstalledPackageEntry>;
|
|
50
|
+
readonly unrecognized: ReadonlyArray<UnrecognizedInstallRootEntry>;
|
|
51
|
+
}
|
|
52
|
+
/** AXM's own interrupted replacement directories beside a canonical package. */
|
|
53
|
+
export declare const isInstallRootStagingName: (name: string) => boolean;
|
|
54
|
+
export interface ObserveInstallRootArgs {
|
|
55
|
+
readonly layout: WorkspaceLayout;
|
|
56
|
+
readonly graph: DesiredStateGraph;
|
|
57
|
+
readonly locks: LockfileReaderService;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Observe the install root. When desired state is incomplete, every installed
|
|
61
|
+
* package counts as reached: no leftover is claimed without full reachability.
|
|
62
|
+
*/
|
|
63
|
+
export declare const observeInstallRoot: ({ layout, graph, locks }: ObserveInstallRootArgs) => Effect.Effect<{
|
|
64
|
+
root: string & import("effect/Brand").Brand<"AbsolutePath">;
|
|
65
|
+
packages: InstalledPackageEntry[];
|
|
66
|
+
leftovers: InstalledPackageEntry[];
|
|
67
|
+
unrecognized: UnrecognizedInstallRootEntry[];
|
|
68
|
+
}, import("./service-interface.ts").WorkspaceLockfileReadFailure, FileSystem.FileSystem | Path.Path>;
|
|
69
|
+
//# sourceMappingURL=install-root.d.ts.map
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install-root inventory.
|
|
3
|
+
*
|
|
4
|
+
* Classifies every entry in a scope's install root (`agent_extensions`): an
|
|
5
|
+
* installed package whose path gives a well-formed extension identity or is
|
|
6
|
+
* proven by an accepted resolution, AXM's own interrupted staging, or an
|
|
7
|
+
* unrecognized entry. Each installed package records whether desired state
|
|
8
|
+
* still reaches it; one that nothing reaches is a leftover.
|
|
9
|
+
*
|
|
10
|
+
* Location inside the install root is AXM's install proof for canonical
|
|
11
|
+
* package directories only. It never proves ownership of agent projections.
|
|
12
|
+
*
|
|
13
|
+
* @experimental This API is unstable and may change without notice.
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
import * as Effect from "effect/Effect";
|
|
17
|
+
import * as FileSystem from "effect/FileSystem";
|
|
18
|
+
import * as Path from "effect/Path";
|
|
19
|
+
import { extensionTypes, isExtensionTypePlural, toExtensionType, toExtensionTypePlural, } from "@agentxm/extension-model/unstable/extensions";
|
|
20
|
+
import { sanitizeName } from "./extension-name.js";
|
|
21
|
+
import { computeExtensionPathsForLayout, extensionPathSourceFromLockEntry, } from "./extension-paths.js";
|
|
22
|
+
/** AXM's own interrupted replacement directories beside a canonical package. */
|
|
23
|
+
export const isInstallRootStagingName = (name) => name.endsWith(".axm-staging") || name.endsWith(".axm-backup");
|
|
24
|
+
const PLATFORM_METADATA_FILES = new Set([".DS_Store", "Thumbs.db"]);
|
|
25
|
+
const isCanonicalName = (name) => sanitizeName(name) === name;
|
|
26
|
+
const isOwnerSegment = (segment) => segment.startsWith("@") && segment.length > 1 && isCanonicalName(segment.slice(1));
|
|
27
|
+
/**
|
|
28
|
+
* `<source>/@<owner>/<plural>/<name>` for acquired packages, or
|
|
29
|
+
* `@<owner>/<plural>/<name>` for user-scope authored packages.
|
|
30
|
+
*/
|
|
31
|
+
const identityOf = (segments) => {
|
|
32
|
+
const [first = "", second = "", third = "", fourth = ""] = segments;
|
|
33
|
+
const shape = segments.length === 4 && !first.startsWith("@")
|
|
34
|
+
? { sourceDirectory: first, owner: second, plural: third, name: fourth }
|
|
35
|
+
: segments.length === 3
|
|
36
|
+
? { sourceDirectory: undefined, owner: first, plural: second, name: third }
|
|
37
|
+
: undefined;
|
|
38
|
+
if (shape === undefined || !isOwnerSegment(shape.owner) || !isCanonicalName(shape.name))
|
|
39
|
+
return undefined;
|
|
40
|
+
if (!isExtensionTypePlural(shape.plural))
|
|
41
|
+
return undefined;
|
|
42
|
+
return {
|
|
43
|
+
type: toExtensionType(shape.plural),
|
|
44
|
+
name: shape.name,
|
|
45
|
+
owner: shape.owner,
|
|
46
|
+
sourceDirectory: shape.sourceDirectory,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/** Whether a directory can still lead to a well-formed identity path. */
|
|
50
|
+
const isIdentityPrefix = (segments) => {
|
|
51
|
+
const [first = "", second = "", third = ""] = segments;
|
|
52
|
+
switch (segments.length) {
|
|
53
|
+
case 1:
|
|
54
|
+
return true;
|
|
55
|
+
case 2:
|
|
56
|
+
return first.startsWith("@")
|
|
57
|
+
? isOwnerSegment(first) && isExtensionTypePlural(second)
|
|
58
|
+
: isOwnerSegment(second);
|
|
59
|
+
case 3:
|
|
60
|
+
return !first.startsWith("@") && isExtensionTypePlural(third);
|
|
61
|
+
default:
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Observe the install root. When desired state is incomplete, every installed
|
|
67
|
+
* package counts as reached: no leftover is claimed without full reachability.
|
|
68
|
+
*/
|
|
69
|
+
export const observeInstallRoot = ({ layout, graph, locks }) => Effect.gen(function* () {
|
|
70
|
+
const fs = yield* FileSystem.FileSystem;
|
|
71
|
+
const path = yield* Path.Path;
|
|
72
|
+
const root = layout.acquiredRoot;
|
|
73
|
+
const accepted = (yield* Effect.forEach(extensionTypes, (type) => locks
|
|
74
|
+
.entries(type)
|
|
75
|
+
.pipe(Effect.map((entries) => Object.entries(entries).map(([key, entry]) => ({ type, key, entry })))))).flat();
|
|
76
|
+
const nodeReaches = (type, key) => graph.nodes.some((node) => node.type === type && (type === "mcp-server" ? node.identity === key : node.name === key));
|
|
77
|
+
const lockedPaths = new Map(accepted.map((row) => [
|
|
78
|
+
computeExtensionPathsForLayout(path.join, layout, extensionPathSourceFromLockEntry(row.entry), toExtensionTypePlural(row.type), row.entry.workspaceName).canonicalPath,
|
|
79
|
+
{ ...row, reached: nodeReaches(row.type, row.key) },
|
|
80
|
+
]));
|
|
81
|
+
// A desired extension whose resolution is not yet accepted may still be
|
|
82
|
+
// materialized into this path, so it reaches every same-named package.
|
|
83
|
+
const unlockedDesired = graph.nodes.filter((node) => !accepted.some((row) => row.type === node.type &&
|
|
84
|
+
(node.type === "mcp-server" ? row.key === node.identity : row.key === node.name)));
|
|
85
|
+
const packages = [];
|
|
86
|
+
const unrecognized = [];
|
|
87
|
+
const isLink = (target) => fs.readLink(target).pipe(Effect.as(true), Effect.orElseSucceed(() => false));
|
|
88
|
+
const leadsToLockedPath = (directory) => [...lockedPaths.keys()].some((locked) => {
|
|
89
|
+
const relative = path.relative(directory, locked);
|
|
90
|
+
return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
91
|
+
});
|
|
92
|
+
const visit = (directory, segments) => Effect.gen(function* () {
|
|
93
|
+
const names = yield* fs.readDirectory(directory).pipe(Effect.orElseSucceed(() => []));
|
|
94
|
+
for (const name of [...names].sort()) {
|
|
95
|
+
const absolute = path.join(directory, name);
|
|
96
|
+
const childSegments = [...segments, name];
|
|
97
|
+
if (isInstallRootStagingName(name) || PLATFORM_METADATA_FILES.has(name))
|
|
98
|
+
continue;
|
|
99
|
+
if (yield* isLink(absolute)) {
|
|
100
|
+
unrecognized.push({ kind: "unrecognized", path: absolute, entryKind: "symlink" });
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const info = yield* fs.stat(absolute).pipe(Effect.option);
|
|
104
|
+
if (info._tag === "None")
|
|
105
|
+
continue;
|
|
106
|
+
if (info.value.type !== "Directory") {
|
|
107
|
+
unrecognized.push({ kind: "unrecognized", path: absolute, entryKind: "file" });
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const locked = lockedPaths.get(absolute);
|
|
111
|
+
const identity = identityOf(childSegments);
|
|
112
|
+
const type = locked?.type ?? identity?.type;
|
|
113
|
+
const packageName = locked?.entry.workspaceName ?? identity?.name;
|
|
114
|
+
if (type !== undefined && packageName !== undefined) {
|
|
115
|
+
// A path without a source directory is authored content, which is
|
|
116
|
+
// never inferred to be undesired.
|
|
117
|
+
const reached = !graph.complete ||
|
|
118
|
+
(locked === undefined && identity?.sourceDirectory === undefined) ||
|
|
119
|
+
(locked?.reached ?? false) ||
|
|
120
|
+
unlockedDesired.some((node) => node.type === type && node.name === packageName);
|
|
121
|
+
packages.push({
|
|
122
|
+
kind: "package",
|
|
123
|
+
type,
|
|
124
|
+
name: packageName,
|
|
125
|
+
owner: identity?.owner,
|
|
126
|
+
sourceDirectory: identity?.sourceDirectory,
|
|
127
|
+
path: absolute,
|
|
128
|
+
lockKey: locked?.key,
|
|
129
|
+
reached,
|
|
130
|
+
});
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (isIdentityPrefix(childSegments) || leadsToLockedPath(absolute)) {
|
|
134
|
+
yield* visit(absolute, childSegments);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
unrecognized.push({ kind: "unrecognized", path: absolute, entryKind: "directory" });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
if (yield* fs.exists(root).pipe(Effect.orElseSucceed(() => false)))
|
|
141
|
+
yield* visit(root, []);
|
|
142
|
+
return {
|
|
143
|
+
root,
|
|
144
|
+
packages,
|
|
145
|
+
leftovers: packages.filter((entry) => !entry.reached),
|
|
146
|
+
unrecognized,
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=install-root.js.map
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
2
|
import { type ConfiguredAgentOutcome } from "../../configured-agent-outcome.js";
|
|
3
3
|
import type { ExtensionKey } from "../types.js";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* How desired state explains one detected extension.
|
|
6
|
+
*
|
|
7
|
+
* - `configured` — declared directly in workspace settings.
|
|
8
|
+
* - `implicit` — reached indirectly, such as through a desired Pack.
|
|
9
|
+
* - `leftover` — an installed package in the install root that desired state
|
|
10
|
+
* no longer reaches.
|
|
11
|
+
* - `undeclared` — an authored package in its standard authoring folder with no
|
|
12
|
+
* workspace declaration.
|
|
13
|
+
* - `unmanaged` — native agent content AXM does not own.
|
|
14
|
+
*/
|
|
15
|
+
export declare const ExtensionInventoryLifecycleSchema: Schema.Literals<readonly ["configured", "implicit", "leftover", "undeclared", "unmanaged"]>;
|
|
5
16
|
export type ExtensionInventoryLifecycle = typeof ExtensionInventoryLifecycleSchema.Type;
|
|
6
17
|
export declare const ExtensionInventoryClassificationSchema: Schema.Struct<{
|
|
7
18
|
readonly kind: Schema.Literal<"lifecycle">;
|
|
8
|
-
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "unmanaged"]>;
|
|
19
|
+
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "leftover", "undeclared", "unmanaged"]>;
|
|
9
20
|
}>;
|
|
10
21
|
export type ExtensionInventoryClassification = typeof ExtensionInventoryClassificationSchema.Type;
|
|
11
22
|
export declare const ExtensionInventoryRowSchema: Schema.Struct<{
|
|
@@ -14,7 +25,7 @@ export declare const ExtensionInventoryRowSchema: Schema.Struct<{
|
|
|
14
25
|
readonly name: Schema.String;
|
|
15
26
|
readonly classification: Schema.Struct<{
|
|
16
27
|
readonly kind: Schema.Literal<"lifecycle">;
|
|
17
|
-
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "unmanaged"]>;
|
|
28
|
+
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "leftover", "undeclared", "unmanaged"]>;
|
|
18
29
|
}>;
|
|
19
30
|
readonly enabled: Schema.NullOr<Schema.Boolean>;
|
|
20
31
|
readonly installed: Schema.Boolean;
|
|
@@ -47,7 +58,7 @@ export declare const ExtensionInventorySchema: Schema.Struct<{
|
|
|
47
58
|
readonly name: Schema.String;
|
|
48
59
|
readonly classification: Schema.Struct<{
|
|
49
60
|
readonly kind: Schema.Literal<"lifecycle">;
|
|
50
|
-
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "unmanaged"]>;
|
|
61
|
+
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "leftover", "undeclared", "unmanaged"]>;
|
|
51
62
|
}>;
|
|
52
63
|
readonly enabled: Schema.NullOr<Schema.Boolean>;
|
|
53
64
|
readonly installed: Schema.Boolean;
|
|
@@ -76,6 +87,8 @@ export declare const ExtensionInventorySchema: Schema.Struct<{
|
|
|
76
87
|
readonly configuredCount: Schema.Number;
|
|
77
88
|
readonly implicitCount: Schema.Number;
|
|
78
89
|
readonly installedCount: Schema.Number;
|
|
90
|
+
readonly leftoverCount: Schema.Number;
|
|
91
|
+
readonly undeclaredCount: Schema.Number;
|
|
79
92
|
readonly unmanagedCount: Schema.Number;
|
|
80
93
|
}>;
|
|
81
94
|
export type ExtensionInventory = typeof ExtensionInventorySchema.Type;
|
|
@@ -95,5 +108,9 @@ export interface ProjectExtensionInventoryInput {
|
|
|
95
108
|
readonly lifecycle: ReadonlyArray<LifecycleInventoryCandidate>;
|
|
96
109
|
readonly agents?: ReadonlyArray<string>;
|
|
97
110
|
}
|
|
111
|
+
/** Whether desired state reaches rows with this lifecycle. */
|
|
112
|
+
export declare const isDesiredInventoryLifecycle: (lifecycle: ExtensionInventoryLifecycle) => boolean;
|
|
113
|
+
/** Aggregate counts for an inventory's already-filtered items. */
|
|
114
|
+
export declare const countExtensionInventory: (items: ReadonlyArray<ExtensionInventoryRow>) => ExtensionInventory;
|
|
98
115
|
export declare const projectExtensionInventory: (input: ProjectExtensionInventoryInput) => ExtensionInventory;
|
|
99
116
|
//# sourceMappingURL=inventory.d.ts.map
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
2
|
import { ExtensionTypeSchema } from "@agentxm/extension-model/unstable/extensions/common";
|
|
3
3
|
import { ConfiguredAgentOutcomeSchema, } from "../../configured-agent-outcome.js";
|
|
4
|
+
/**
|
|
5
|
+
* How desired state explains one detected extension.
|
|
6
|
+
*
|
|
7
|
+
* - `configured` — declared directly in workspace settings.
|
|
8
|
+
* - `implicit` — reached indirectly, such as through a desired Pack.
|
|
9
|
+
* - `leftover` — an installed package in the install root that desired state
|
|
10
|
+
* no longer reaches.
|
|
11
|
+
* - `undeclared` — an authored package in its standard authoring folder with no
|
|
12
|
+
* workspace declaration.
|
|
13
|
+
* - `unmanaged` — native agent content AXM does not own.
|
|
14
|
+
*/
|
|
4
15
|
export const ExtensionInventoryLifecycleSchema = Schema.Literals([
|
|
5
16
|
"configured",
|
|
6
17
|
"implicit",
|
|
18
|
+
"leftover",
|
|
19
|
+
"undeclared",
|
|
7
20
|
"unmanaged",
|
|
8
21
|
]);
|
|
9
22
|
export const ExtensionInventoryClassificationSchema = Schema.Struct({
|
|
@@ -35,12 +48,32 @@ export const ExtensionInventorySchema = Schema.Struct({
|
|
|
35
48
|
configuredCount: Schema.Number,
|
|
36
49
|
implicitCount: Schema.Number,
|
|
37
50
|
installedCount: Schema.Number,
|
|
51
|
+
leftoverCount: Schema.Number,
|
|
52
|
+
undeclaredCount: Schema.Number,
|
|
38
53
|
unmanagedCount: Schema.Number,
|
|
39
54
|
});
|
|
40
55
|
const lifecyclePriority = {
|
|
41
56
|
configured: 0,
|
|
42
57
|
implicit: 1,
|
|
43
|
-
|
|
58
|
+
leftover: 2,
|
|
59
|
+
undeclared: 3,
|
|
60
|
+
unmanaged: 4,
|
|
61
|
+
};
|
|
62
|
+
/** Whether desired state reaches rows with this lifecycle. */
|
|
63
|
+
export const isDesiredInventoryLifecycle = (lifecycle) => lifecycle === "configured" || lifecycle === "implicit";
|
|
64
|
+
/** Aggregate counts for an inventory's already-filtered items. */
|
|
65
|
+
export const countExtensionInventory = (items) => {
|
|
66
|
+
const withLifecycle = (lifecycle) => items.filter((item) => item.classification.lifecycle === lifecycle).length;
|
|
67
|
+
return {
|
|
68
|
+
items,
|
|
69
|
+
count: items.length,
|
|
70
|
+
configuredCount: withLifecycle("configured"),
|
|
71
|
+
implicitCount: withLifecycle("implicit"),
|
|
72
|
+
installedCount: items.filter((item) => item.installed).length,
|
|
73
|
+
leftoverCount: withLifecycle("leftover"),
|
|
74
|
+
undeclaredCount: withLifecycle("undeclared"),
|
|
75
|
+
unmanagedCount: withLifecycle("unmanaged"),
|
|
76
|
+
};
|
|
44
77
|
};
|
|
45
78
|
const keyString = (key) => `${key.scope}:${key.type}:${key.name}`;
|
|
46
79
|
const addAll = (target, values) => {
|
|
@@ -95,17 +128,6 @@ export const projectExtensionInventory = (input) => {
|
|
|
95
128
|
origins: sorted(aggregate.origins),
|
|
96
129
|
paths: sorted(aggregate.paths),
|
|
97
130
|
}));
|
|
98
|
-
|
|
99
|
-
const configuredCount = items.filter((item) => item.classification.lifecycle === "configured").length;
|
|
100
|
-
const implicitCount = items.filter((item) => item.classification.lifecycle === "implicit").length;
|
|
101
|
-
const unmanagedCount = items.filter((item) => item.classification.lifecycle === "unmanaged").length;
|
|
102
|
-
return {
|
|
103
|
-
items,
|
|
104
|
-
count: items.length,
|
|
105
|
-
configuredCount,
|
|
106
|
-
implicitCount,
|
|
107
|
-
installedCount: items.filter((item) => item.installed).length,
|
|
108
|
-
unmanagedCount,
|
|
109
|
-
};
|
|
131
|
+
return countExtensionInventory(lifecycleRows.sort((left, right) => left.name.localeCompare(right.name)));
|
|
110
132
|
};
|
|
111
133
|
//# sourceMappingURL=inventory.js.map
|
|
@@ -37,6 +37,7 @@ import { MANIFEST_FILENAME_BY_TYPE } from "@agentxm/extension-content";
|
|
|
37
37
|
import { parseSkillMd } from "@agentxm/extension-content";
|
|
38
38
|
import { DISCOVERY_SKIPPED_DIRECTORIES } from "@agentxm/extension-model/unstable/discovery-walk";
|
|
39
39
|
import { makeAbsolutePath } from "@agentxm/extension-model/unstable/path-types";
|
|
40
|
+
import { isInstallRootStagingName } from "../../install-root.js";
|
|
40
41
|
import { childEntries, fileExists, filterDirectories, splitAbsolutePathSegments, } from "./fs-helpers.js";
|
|
41
42
|
const SCANNER_NAME = "canonical-extensions";
|
|
42
43
|
/**
|
|
@@ -187,7 +188,7 @@ const scanAcquiredDirectory = (deps, dir) => Effect.gen(function* () {
|
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
190
|
const childCandidates = entries
|
|
190
|
-
.filter((entry) => !DISCOVERY_SKIPPED_DIRECTORIES.has(entry))
|
|
191
|
+
.filter((entry) => !DISCOVERY_SKIPPED_DIRECTORIES.has(entry) && !isInstallRootStagingName(entry))
|
|
191
192
|
.map((entry) => deps.path.join(dir, entry));
|
|
192
193
|
const childDirs = yield* filterDirectories(deps.fs, childCandidates);
|
|
193
194
|
const nested = yield* Effect.forEach(childDirs, (childDir) => scanAcquiredDirectory(deps, childDir), { concurrency: "unbounded" });
|
|
@@ -8,6 +8,7 @@ import * as Path from "effect/Path";
|
|
|
8
8
|
import { type Handle } from "@agentxm/extension-model/unstable/extensions/handle";
|
|
9
9
|
import type { SourceHostConfig } from "../../settings/schema.js";
|
|
10
10
|
import { type AbsolutePath } from "@agentxm/extension-model/unstable/path-types";
|
|
11
|
+
import { type WorkspaceLayout } from "../layout.js";
|
|
11
12
|
import { AgentRootResolver } from "./agent-root-resolver.js";
|
|
12
13
|
import { type ScopedAgentsApi } from "./agents/index.js";
|
|
13
14
|
import { type Warning } from "./diagnostics.js";
|
|
@@ -55,6 +56,8 @@ export interface WorkspaceReadModel {
|
|
|
55
56
|
readonly owner: ScopedOwnerApi;
|
|
56
57
|
readonly diagnostics: Effect.Effect<ReadonlyArray<Warning>>;
|
|
57
58
|
readonly canonicalExtensions: Effect.Effect<ReadonlyArray<CanonicalExtensionOccurrence>>;
|
|
59
|
+
/** The resolved storage layout, or none when the layout could not be resolved. */
|
|
60
|
+
readonly layout: Option.Option<WorkspaceLayout>;
|
|
58
61
|
}
|
|
59
62
|
/**
|
|
60
63
|
* The read-model family that carries each catalog extension type, or `null`
|
|
@@ -343,6 +343,7 @@ const buildScope = Effect.fn("workspace.read-model.build-scope")(function* (deps
|
|
|
343
343
|
owner,
|
|
344
344
|
diagnostics: diagnostics.snapshot,
|
|
345
345
|
canonicalExtensions: canonicalScanner,
|
|
346
|
+
layout: Result.isFailure(layoutResult) ? Option.none() : Option.some(layoutResult.success),
|
|
346
347
|
};
|
|
347
348
|
});
|
|
348
349
|
// ---------------------------------------------------------------------------
|
|
@@ -10,7 +10,7 @@ import { installableExtensionTypes, } from "@agentxm/extension-model/unstable/ex
|
|
|
10
10
|
import { isAxmManagedMcpEntry } from "@agentxm/agent-integration";
|
|
11
11
|
import { createDefaultSettings } from "../settings/index.js";
|
|
12
12
|
import { configuredAgentLifecycleOutcomes } from "./configured-agent-outcomes.js";
|
|
13
|
-
import { projectExtensionInventory, } from "./read-model/extensions/inventory.js";
|
|
13
|
+
import { countExtensionInventory, isDesiredInventoryLifecycle, projectExtensionInventory, } from "./read-model/extensions/inventory.js";
|
|
14
14
|
import { deriveSourceMetaFromLockType } from "./source-metadata.js";
|
|
15
15
|
export const makeReadModelRecordReaders = (args) => {
|
|
16
16
|
const packagingKindForSource = (type, source) => {
|
|
@@ -88,9 +88,38 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
88
88
|
paths: observations.paths,
|
|
89
89
|
};
|
|
90
90
|
};
|
|
91
|
-
const
|
|
91
|
+
const isWithin = (root, candidate) => {
|
|
92
|
+
const relative = args.path.relative(root, candidate);
|
|
93
|
+
return !relative.startsWith("..") && !args.path.isAbsolute(relative);
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Classify one occurrence desired state does not explain: an AXM package
|
|
97
|
+
* under the install root is `leftover`, an AXM package under its type's
|
|
98
|
+
* standard authoring folder is `undeclared`, and anything else — native
|
|
99
|
+
* agent content — is `unmanaged`.
|
|
100
|
+
*/
|
|
101
|
+
const unexplainedLifecycle = (layout, key, actual) => {
|
|
102
|
+
if (Option.isNone(layout) || typeof actual !== "object" || actual === null)
|
|
103
|
+
return "unmanaged";
|
|
104
|
+
const originTag = stringProperty("origin" in actual ? actual.origin : undefined, "_tag");
|
|
105
|
+
const packageLocation = stringProperty(actual, "packageRoot") ?? stringProperty(actual, "contentRoot");
|
|
106
|
+
if (originTag === null || packageLocation === null)
|
|
107
|
+
return "unmanaged";
|
|
108
|
+
const canonical = originTag.startsWith("canonical-axm-");
|
|
109
|
+
if (!canonical && !originTag.startsWith("external-axm-"))
|
|
110
|
+
return "unmanaged";
|
|
111
|
+
if (isWithin(layout.value.acquiredRoot, packageLocation))
|
|
112
|
+
return "leftover";
|
|
113
|
+
if (canonical &&
|
|
114
|
+
layout.value.scope === "project" &&
|
|
115
|
+
isWithin(layout.value.authoredRoot(key.type), packageLocation)) {
|
|
116
|
+
return "undeclared";
|
|
117
|
+
}
|
|
118
|
+
return "unmanaged";
|
|
119
|
+
};
|
|
120
|
+
const lifecycleCandidateFromUnexplained = (layout, row) => ({
|
|
92
121
|
key: row.key,
|
|
93
|
-
lifecycle:
|
|
122
|
+
lifecycle: unexplainedLifecycle(layout, row.key, row.actual),
|
|
94
123
|
enabled: null,
|
|
95
124
|
installed: true,
|
|
96
125
|
...observationFromActual(row.actual),
|
|
@@ -273,7 +302,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
273
302
|
paths: [],
|
|
274
303
|
}));
|
|
275
304
|
const implicitObserved = desiredUnmanaged.map((row) => ({
|
|
276
|
-
...
|
|
305
|
+
...lifecycleCandidateFromUnexplained(input.layout, row),
|
|
277
306
|
lifecycle: "implicit",
|
|
278
307
|
enabled: true,
|
|
279
308
|
}));
|
|
@@ -287,8 +316,8 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
287
316
|
...implicitMissing,
|
|
288
317
|
...input.unmanaged
|
|
289
318
|
.filter((row) => !desiredPackMembers.has(row.key.name))
|
|
290
|
-
.map(
|
|
291
|
-
...stalePackActuals.map(
|
|
319
|
+
.map((row) => lifecycleCandidateFromUnexplained(input.layout, row)),
|
|
320
|
+
...stalePackActuals.map((row) => lifecycleCandidateFromUnexplained(input.layout, row)),
|
|
292
321
|
],
|
|
293
322
|
agents: input.agents,
|
|
294
323
|
});
|
|
@@ -304,9 +333,8 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
304
333
|
const withOutcomes = inventory.items.map((row) => {
|
|
305
334
|
return {
|
|
306
335
|
...row,
|
|
307
|
-
agentOutcomes: row.classification.lifecycle
|
|
308
|
-
?
|
|
309
|
-
: configuredAgentLifecycleOutcomes({
|
|
336
|
+
agentOutcomes: isDesiredInventoryLifecycle(row.classification.lifecycle)
|
|
337
|
+
? configuredAgentLifecycleOutcomes({
|
|
310
338
|
type: row.type,
|
|
311
339
|
name: row.name,
|
|
312
340
|
agentIds: configuredAgents,
|
|
@@ -315,22 +343,14 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
315
343
|
targetState: row.enabled === false ? "disabled" : "enabled",
|
|
316
344
|
installed: row.installed,
|
|
317
345
|
observedAgentIds: row.agents,
|
|
318
|
-
})
|
|
346
|
+
})
|
|
347
|
+
: [],
|
|
319
348
|
};
|
|
320
349
|
});
|
|
321
350
|
const items = withOutcomes.filter((row) => agents.length === 0 ||
|
|
322
351
|
agents.some((agentId) => row.agents.includes(agentId) ||
|
|
323
352
|
row.agentOutcomes.some((outcome) => outcome.agentId === agentId)));
|
|
324
|
-
return
|
|
325
|
-
items,
|
|
326
|
-
count: items.length,
|
|
327
|
-
configuredCount: items.filter((item) => item.classification.lifecycle === "configured").length,
|
|
328
|
-
implicitCount: items.filter((item) => item.classification.lifecycle === "implicit")
|
|
329
|
-
.length,
|
|
330
|
-
installedCount: items.filter((item) => item.installed).length,
|
|
331
|
-
unmanagedCount: items.filter((item) => item.classification.lifecycle === "unmanaged")
|
|
332
|
-
.length,
|
|
333
|
-
};
|
|
353
|
+
return countExtensionInventory(items);
|
|
334
354
|
};
|
|
335
355
|
switch (type) {
|
|
336
356
|
case "skill": {
|
|
@@ -339,6 +359,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
339
359
|
const unmanaged = yield* scoped.skills.unmanaged;
|
|
340
360
|
return finalizeInventory(projectStandardInventory({
|
|
341
361
|
scope: scoped.scope,
|
|
362
|
+
layout: scoped.layout,
|
|
342
363
|
type,
|
|
343
364
|
installed,
|
|
344
365
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -354,6 +375,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
354
375
|
const unmanaged = yield* scoped.mcpServers.unmanaged;
|
|
355
376
|
return finalizeInventory(projectStandardInventory({
|
|
356
377
|
scope: scoped.scope,
|
|
378
|
+
layout: scoped.layout,
|
|
357
379
|
type,
|
|
358
380
|
installed,
|
|
359
381
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -369,6 +391,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
369
391
|
const unmanaged = yield* scoped.subagents.unmanaged;
|
|
370
392
|
return finalizeInventory(projectStandardInventory({
|
|
371
393
|
scope: scoped.scope,
|
|
394
|
+
layout: scoped.layout,
|
|
372
395
|
type,
|
|
373
396
|
installed,
|
|
374
397
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -384,6 +407,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
384
407
|
const unmanaged = yield* scoped.packs.unmanaged;
|
|
385
408
|
return finalizeInventory(projectStandardInventory({
|
|
386
409
|
scope: scoped.scope,
|
|
410
|
+
layout: scoped.layout,
|
|
387
411
|
type,
|
|
388
412
|
installed,
|
|
389
413
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -399,6 +423,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
399
423
|
const unmanaged = yield* scoped.rules.unmanaged;
|
|
400
424
|
return finalizeInventory(projectStandardInventory({
|
|
401
425
|
scope: scoped.scope,
|
|
426
|
+
layout: scoped.layout,
|
|
402
427
|
type,
|
|
403
428
|
installed,
|
|
404
429
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -414,6 +439,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
414
439
|
const unmanaged = yield* scoped.hooks.unmanaged;
|
|
415
440
|
return finalizeInventory(projectStandardInventory({
|
|
416
441
|
scope: scoped.scope,
|
|
442
|
+
layout: scoped.layout,
|
|
417
443
|
type,
|
|
418
444
|
installed,
|
|
419
445
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -429,6 +455,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
429
455
|
const unmanaged = yield* scoped.knowledge.unmanaged;
|
|
430
456
|
return finalizeInventory(projectStandardInventory({
|
|
431
457
|
scope: scoped.scope,
|
|
458
|
+
layout: scoped.layout,
|
|
432
459
|
type,
|
|
433
460
|
installed,
|
|
434
461
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -457,6 +484,8 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
457
484
|
configuredCount: inventories.reduce((total, inventory) => total + inventory.configuredCount, 0),
|
|
458
485
|
implicitCount: inventories.reduce((total, inventory) => total + inventory.implicitCount, 0),
|
|
459
486
|
installedCount: inventories.reduce((total, inventory) => total + inventory.installedCount, 0),
|
|
487
|
+
leftoverCount: inventories.reduce((total, inventory) => total + inventory.leftoverCount, 0),
|
|
488
|
+
undeclaredCount: inventories.reduce((total, inventory) => total + inventory.undeclaredCount, 0),
|
|
460
489
|
unmanagedCount: inventories.reduce((total, inventory) => total + inventory.unmanagedCount, 0),
|
|
461
490
|
};
|
|
462
491
|
});
|
|
@@ -106,7 +106,7 @@ export type WorkspaceStateReadFailure = SettingsReadError | LockfileReadError |
|
|
|
106
106
|
*/
|
|
107
107
|
export type WorkspaceSettingsMutationFailure = WorkspaceSettingsReadFailure | SettingsWriteError | WorkspaceSnapshotError;
|
|
108
108
|
/** Lockfile read-modify-write through the snapshot-commit path. */
|
|
109
|
-
export type WorkspaceLockfileMutationFailure = WorkspaceLockfileReadFailure | LockfileValidationError | LockfileWriteError;
|
|
109
|
+
export type WorkspaceLockfileMutationFailure = WorkspaceLockfileReadFailure | LockfileValidationError | LockfileWriteError | WorkspaceSnapshotError;
|
|
110
110
|
/** Coupled settings-and-lockfile mutation. */
|
|
111
111
|
export type WorkspaceStateMutationFailure = WorkspaceSettingsMutationFailure | WorkspaceLockfileMutationFailure;
|
|
112
112
|
export interface WorkspaceReadModelRecords {
|
|
@@ -61,6 +61,8 @@ export declare const readModelRecordStubs: {
|
|
|
61
61
|
configuredCount: number;
|
|
62
62
|
implicitCount: number;
|
|
63
63
|
installedCount: number;
|
|
64
|
+
leftoverCount: number;
|
|
65
|
+
undeclaredCount: number;
|
|
64
66
|
unmanagedCount: number;
|
|
65
67
|
}, never, never>;
|
|
66
68
|
readonly getExtensionInventory: () => Effect.Effect<ExtensionInventory, WorkspaceStateReadFailure>;
|
|
@@ -23,6 +23,8 @@ const emptyInventory = () => Effect.succeed({
|
|
|
23
23
|
configuredCount: 0,
|
|
24
24
|
implicitCount: 0,
|
|
25
25
|
installedCount: 0,
|
|
26
|
+
leftoverCount: 0,
|
|
27
|
+
undeclaredCount: 0,
|
|
26
28
|
unmanagedCount: 0,
|
|
27
29
|
});
|
|
28
30
|
const fs = (() => {
|
|
@@ -94,6 +96,8 @@ export const readModelRecordStubs = {
|
|
|
94
96
|
configuredCount: 0,
|
|
95
97
|
implicitCount: 0,
|
|
96
98
|
installedCount: 0,
|
|
99
|
+
leftoverCount: 0,
|
|
100
|
+
undeclaredCount: 0,
|
|
97
101
|
unmanagedCount: 0,
|
|
98
102
|
}),
|
|
99
103
|
getExtensionInventory: emptyInventory,
|
package/package.json
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
"url": "https://github.com/agentxm/axm/issues"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@agentxm/agent-integration": "^0.30.
|
|
8
|
-
"@agentxm/extension-content": "^0.30.
|
|
9
|
-
"@agentxm/extension-model": "^0.30.
|
|
10
|
-
"@agentxm/registry-protocol": "^0.30.
|
|
11
|
-
"@agentxm/workspace-transactions": "^0.30.
|
|
7
|
+
"@agentxm/agent-integration": "^0.30.2",
|
|
8
|
+
"@agentxm/extension-content": "^0.30.2",
|
|
9
|
+
"@agentxm/extension-model": "^0.30.2",
|
|
10
|
+
"@agentxm/registry-protocol": "^0.30.2",
|
|
11
|
+
"@agentxm/workspace-transactions": "^0.30.2",
|
|
12
12
|
"effect": "4.0.0-rc.115",
|
|
13
13
|
"jsonc-parser": "^3.3.1",
|
|
14
14
|
"semver": "^7.8.5",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"description": "AXM workspace state kernel: settings, lockfile, read model, and desired-state vocabulary for the axm CLI. Unstable and unsupported — use the axm.sh CLI.",
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@agentxm/specification-metadata": "^0.30.
|
|
20
|
+
"@agentxm/specification-metadata": "^0.30.2",
|
|
21
21
|
"@effect/platform-node": "4.0.0-rc.115",
|
|
22
22
|
"@effect/vitest": "4.0.0-rc.115",
|
|
23
23
|
"@fast-check/vitest": "^0.5.0",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
},
|
|
64
64
|
"sideEffects": false,
|
|
65
65
|
"type": "module",
|
|
66
|
-
"version": "0.30.
|
|
66
|
+
"version": "0.30.2"
|
|
67
67
|
}
|