@agentxm/extension-sources 0.28.4-bootstrap.0
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 +9 -0
- package/dist/src/axm-skill-gate.d.ts +64 -0
- package/dist/src/axm-skill-gate.js +25 -0
- package/dist/src/errors.d.ts +116 -0
- package/dist/src/errors.js +98 -0
- package/dist/src/failure-category.d.ts +10 -0
- package/dist/src/failure-category.js +2 -0
- package/dist/src/file-url.d.ts +9 -0
- package/dist/src/file-url.js +12 -0
- package/dist/src/git/detect.d.ts +7 -0
- package/dist/src/git/detect.js +23 -0
- package/dist/src/git/operations.d.ts +37 -0
- package/dist/src/git/operations.js +101 -0
- package/dist/src/glob.d.ts +39 -0
- package/dist/src/glob.js +79 -0
- package/dist/src/hook-package-discovery.d.ts +19 -0
- package/dist/src/hook-package-discovery.js +14 -0
- package/dist/src/index.d.ts +30 -0
- package/dist/src/index.js +35 -0
- package/dist/src/integrity.d.ts +13 -0
- package/dist/src/integrity.js +17 -0
- package/dist/src/live.d.ts +29 -0
- package/dist/src/live.js +131 -0
- package/dist/src/manifest-package-discovery.d.ts +28 -0
- package/dist/src/manifest-package-discovery.js +96 -0
- package/dist/src/package-discovery.d.ts +19 -0
- package/dist/src/package-discovery.js +137 -0
- package/dist/src/package-sources.d.ts +25 -0
- package/dist/src/package-sources.js +102 -0
- package/dist/src/providers/azurerepos/index.d.ts +11 -0
- package/dist/src/providers/azurerepos/index.js +11 -0
- package/dist/src/providers/azurerepos/repo-exists.d.ts +5 -0
- package/dist/src/providers/azurerepos/repo-exists.js +21 -0
- package/dist/src/providers/azurerepos/scp.d.ts +12 -0
- package/dist/src/providers/azurerepos/scp.js +29 -0
- package/dist/src/providers/azurerepos/shorthand.d.ts +12 -0
- package/dist/src/providers/azurerepos/shorthand.js +45 -0
- package/dist/src/providers/azurerepos/url.d.ts +14 -0
- package/dist/src/providers/azurerepos/url.js +38 -0
- package/dist/src/providers/bitbucket/index.d.ts +11 -0
- package/dist/src/providers/bitbucket/index.js +11 -0
- package/dist/src/providers/bitbucket/resolve-repo.d.ts +11 -0
- package/dist/src/providers/bitbucket/resolve-repo.js +33 -0
- package/dist/src/providers/bitbucket/scp.d.ts +11 -0
- package/dist/src/providers/bitbucket/scp.js +28 -0
- package/dist/src/providers/bitbucket/shorthand.d.ts +10 -0
- package/dist/src/providers/bitbucket/shorthand.js +11 -0
- package/dist/src/providers/bitbucket/url.d.ts +13 -0
- package/dist/src/providers/bitbucket/url.js +42 -0
- package/dist/src/providers/convention-discovery.d.ts +11 -0
- package/dist/src/providers/convention-discovery.js +473 -0
- package/dist/src/providers/git-hosting.d.ts +31 -0
- package/dist/src/providers/git-hosting.js +73 -0
- package/dist/src/providers/git.d.ts +22 -0
- package/dist/src/providers/git.js +46 -0
- package/dist/src/providers/github/index.d.ts +11 -0
- package/dist/src/providers/github/index.js +11 -0
- package/dist/src/providers/github/resolve-repo.d.ts +11 -0
- package/dist/src/providers/github/resolve-repo.js +34 -0
- package/dist/src/providers/github/scp.d.ts +11 -0
- package/dist/src/providers/github/scp.js +28 -0
- package/dist/src/providers/github/shorthand.d.ts +10 -0
- package/dist/src/providers/github/shorthand.js +11 -0
- package/dist/src/providers/github/url.d.ts +13 -0
- package/dist/src/providers/github/url.js +42 -0
- package/dist/src/providers/gitlab/index.d.ts +11 -0
- package/dist/src/providers/gitlab/index.js +11 -0
- package/dist/src/providers/gitlab/resolve-repo.d.ts +11 -0
- package/dist/src/providers/gitlab/resolve-repo.js +33 -0
- package/dist/src/providers/gitlab/scp.d.ts +11 -0
- package/dist/src/providers/gitlab/scp.js +28 -0
- package/dist/src/providers/gitlab/shorthand.d.ts +10 -0
- package/dist/src/providers/gitlab/shorthand.js +11 -0
- package/dist/src/providers/gitlab/url.d.ts +13 -0
- package/dist/src/providers/gitlab/url.js +42 -0
- package/dist/src/providers/local-parser/index.d.ts +8 -0
- package/dist/src/providers/local-parser/index.js +8 -0
- package/dist/src/providers/local-parser/parser.d.ts +13 -0
- package/dist/src/providers/local-parser/parser.js +14 -0
- package/dist/src/providers/local.d.ts +23 -0
- package/dist/src/providers/local.js +39 -0
- package/dist/src/providers/parse-provider-shorthand.d.ts +18 -0
- package/dist/src/providers/parse-provider-shorthand.js +74 -0
- package/dist/src/providers/registry/host-provider.d.ts +47 -0
- package/dist/src/providers/registry/host-provider.js +584 -0
- package/dist/src/resolve-identifier.d.ts +73 -0
- package/dist/src/resolve-identifier.js +273 -0
- package/dist/src/resolve-source-pattern.d.ts +29 -0
- package/dist/src/resolve-source-pattern.js +77 -0
- package/dist/src/resolve-source.d.ts +79 -0
- package/dist/src/resolve-source.js +503 -0
- package/dist/src/rule-package-discovery.d.ts +19 -0
- package/dist/src/rule-package-discovery.js +14 -0
- package/dist/src/service.d.ts +72 -0
- package/dist/src/service.js +109 -0
- package/dist/src/url-fragment.d.ts +5 -0
- package/dist/src/url-fragment.js +19 -0
- package/dist/src/workspace-catalog.d.ts +79 -0
- package/dist/src/workspace-catalog.js +26 -0
- package/package.json +57 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import * as Result from "effect/Result";
|
|
4
|
+
import * as Schema from "effect/Schema";
|
|
5
|
+
import { SourceSyntaxInvalid } from "../../errors.js";
|
|
6
|
+
import { BitbucketSourceParamsSchema, } from "@agentxm/extension-model/unstable/sources/types";
|
|
7
|
+
import { refFromUrlHash } from "../../url-fragment.js";
|
|
8
|
+
export const CANONICAL_HOSTNAME = "bitbucket.org";
|
|
9
|
+
/** Matches: /owner/repo[/src/ref/path] */
|
|
10
|
+
const BITBUCKET_PATH_PATTERN = /^\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/src\/([^/]+)(?:\/(.+))?)?$/;
|
|
11
|
+
const decodeBitbucketSourceParams = Schema.decodeUnknownResult(BitbucketSourceParamsSchema);
|
|
12
|
+
export const parseUrl = (url, hostname = CANONICAL_HOSTNAME) => {
|
|
13
|
+
if (url.hostname !== hostname) {
|
|
14
|
+
return Effect.fail(new SourceSyntaxInvalid({
|
|
15
|
+
detail: "Invalid Bitbucket URL format",
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
const match = url.pathname.match(BITBUCKET_PATH_PATTERN);
|
|
19
|
+
if (!match || !match[1] || !match[2]) {
|
|
20
|
+
return Effect.fail(new SourceSyntaxInvalid({
|
|
21
|
+
detail: "Invalid Bitbucket URL format",
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
const fragmentRef = Option.getOrUndefined(refFromUrlHash(url));
|
|
25
|
+
const decoded = decodeBitbucketSourceParams({
|
|
26
|
+
type: "bitbucket",
|
|
27
|
+
owner: match[1],
|
|
28
|
+
repo: match[2],
|
|
29
|
+
...(match[3] === undefined
|
|
30
|
+
? fragmentRef === undefined
|
|
31
|
+
? {}
|
|
32
|
+
: { ref: fragmentRef }
|
|
33
|
+
: { ref: match[3] }),
|
|
34
|
+
...(match[4] === undefined ? {} : { subPath: match[4] }),
|
|
35
|
+
});
|
|
36
|
+
return Result.isSuccess(decoded)
|
|
37
|
+
? Effect.succeed(decoded.success)
|
|
38
|
+
: Effect.fail(new SourceSyntaxInvalid({
|
|
39
|
+
detail: "Invalid Bitbucket URL format",
|
|
40
|
+
}));
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as FileSystem from "effect/FileSystem";
|
|
3
|
+
import * as Path from "effect/Path";
|
|
4
|
+
import { type SourceError } from "../errors.js";
|
|
5
|
+
import { type ExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/extension-ref";
|
|
6
|
+
import type { FindOptions } from "@agentxm/extension-model/unstable/sources/source-host-provider";
|
|
7
|
+
import type { GitBasedSource, LocalSource } from "@agentxm/extension-model/unstable/sources/types";
|
|
8
|
+
type ExternalSource = GitBasedSource | LocalSource;
|
|
9
|
+
export declare const discoverConventionRefs: (source: ExternalSource, basePath: string, options: FindOptions) => Effect.Effect<ReadonlyArray<ExtensionRef>, SourceError, FileSystem.FileSystem | Path.Path>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=convention-discovery.d.ts.map
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import * as Array from "effect/Array";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as FileSystem from "effect/FileSystem";
|
|
4
|
+
import * as Option from "effect/Option";
|
|
5
|
+
import * as Path from "effect/Path";
|
|
6
|
+
import * as Schema from "effect/Schema";
|
|
7
|
+
import { SourceNotResolvable } from "../errors.js";
|
|
8
|
+
import { decodeExtensionNameSync, } from "@agentxm/extension-model/unstable/extensions";
|
|
9
|
+
import {} from "@agentxm/extension-model/unstable/extensions/refs/extension-ref";
|
|
10
|
+
import { discoverManifestPackagesInDir } from "../manifest-package-discovery.js";
|
|
11
|
+
import { DISCOVERY_MAX_DEPTH, DISCOVERY_SKIPPED_DIRECTORIES, } from "@agentxm/extension-model/unstable/discovery-walk";
|
|
12
|
+
import { getCommitSha, getTreeSha } from "../git/operations.js";
|
|
13
|
+
import { hookPackagesInDir } from "../hook-package-discovery.js";
|
|
14
|
+
import {} from "@agentxm/extension-model/unstable/extensions/refs/hook";
|
|
15
|
+
import { KNOWLEDGE_MANIFEST_FILENAME, KnowledgeManifestSchema, } from "@agentxm/extension-model/unstable/knowledge";
|
|
16
|
+
import {} from "@agentxm/extension-model/unstable/extensions/refs/knowledge";
|
|
17
|
+
import { rulePackagesInDir } from "../rule-package-discovery.js";
|
|
18
|
+
import {} from "@agentxm/extension-model/unstable/extensions/refs/rule";
|
|
19
|
+
import { MANIFEST_FILENAME, SubagentManifestSchema, } from "@agentxm/extension-model/unstable/subagents/manifest-schema";
|
|
20
|
+
import { MANIFEST_FILENAME as SKILL_MANIFEST_FILENAME, SkillManifestSchema, } from "@agentxm/extension-model/unstable/skills/manifest-schema";
|
|
21
|
+
import { parseSkillMd } from "@agentxm/registry-protocol/unstable/content";
|
|
22
|
+
import {} from "@agentxm/extension-model/unstable/extensions/refs/skill";
|
|
23
|
+
import { fileUrlToPath } from "../file-url.js";
|
|
24
|
+
// Directory reads reuse the repository's existing sixteen-read archive cap.
|
|
25
|
+
// Git metadata probes stay serial because they spawn subprocesses and no
|
|
26
|
+
// higher subprocess capacity has been established.
|
|
27
|
+
const FILESYSTEM_DISCOVERY_CONCURRENCY = 16;
|
|
28
|
+
const GIT_METADATA_CONCURRENCY = 1;
|
|
29
|
+
const matchesIdentity = (candidate, options) => {
|
|
30
|
+
const nameMatch = options.names.length === 0 || options.names.includes(candidate.name);
|
|
31
|
+
const ownerMatch = Option.isNone(options.owner) ||
|
|
32
|
+
(candidate.owner !== undefined && candidate.owner === options.owner.value);
|
|
33
|
+
return nameMatch && ownerMatch;
|
|
34
|
+
};
|
|
35
|
+
const relativeDir = (basePath, location) => Effect.gen(function* () {
|
|
36
|
+
const path = yield* Path.Path;
|
|
37
|
+
return path.relative(basePath, fileUrlToPath(location));
|
|
38
|
+
});
|
|
39
|
+
const gitTreeShaFor = (source, basePath, location) => relativeDir(basePath, location).pipe(Effect.flatMap((dir) => getTreeSha(basePath, dir)));
|
|
40
|
+
const gitCommitShaFor = (basePath) => getCommitSha(basePath);
|
|
41
|
+
const subPathForSource = (source) => {
|
|
42
|
+
switch (source.type) {
|
|
43
|
+
case "github":
|
|
44
|
+
case "gitlab":
|
|
45
|
+
case "bitbucket":
|
|
46
|
+
case "azurerepos":
|
|
47
|
+
return source.subPath;
|
|
48
|
+
case "git":
|
|
49
|
+
case "local":
|
|
50
|
+
return Option.none();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const discoverSkillPackagesInDir = discoverManifestPackagesInDir({
|
|
54
|
+
type: "skill",
|
|
55
|
+
manifestFilename: SKILL_MANIFEST_FILENAME,
|
|
56
|
+
manifestSchema: SkillManifestSchema,
|
|
57
|
+
});
|
|
58
|
+
const readPortableSkillDiscovery = (dir) => Effect.gen(function* () {
|
|
59
|
+
const fs = yield* FileSystem.FileSystem;
|
|
60
|
+
const path = yield* Path.Path;
|
|
61
|
+
const hasPackageManifest = yield* fs
|
|
62
|
+
.exists(path.join(dir, SKILL_MANIFEST_FILENAME))
|
|
63
|
+
.pipe(Effect.catch(() => Effect.succeed(false)));
|
|
64
|
+
const isAxmPackageContent = path.basename(dir) === "src" &&
|
|
65
|
+
(yield* fs
|
|
66
|
+
.exists(path.join(path.dirname(dir), SKILL_MANIFEST_FILENAME))
|
|
67
|
+
.pipe(Effect.catch(() => Effect.succeed(false))));
|
|
68
|
+
if (hasPackageManifest || isAxmPackageContent) {
|
|
69
|
+
return Option.none();
|
|
70
|
+
}
|
|
71
|
+
const content = yield* fs.readFileString(path.join(dir, "SKILL.md")).pipe(Effect.option);
|
|
72
|
+
if (Option.isNone(content))
|
|
73
|
+
return Option.none();
|
|
74
|
+
const skill = parseSkillMd(content.value, path.basename(dir));
|
|
75
|
+
if (Option.isNone(skill))
|
|
76
|
+
return Option.none();
|
|
77
|
+
const name = decodeExtensionNameSync(skill.value.name);
|
|
78
|
+
return Option.some({
|
|
79
|
+
type: "skill",
|
|
80
|
+
name,
|
|
81
|
+
skill: {
|
|
82
|
+
name,
|
|
83
|
+
description: Option.some(skill.value.description),
|
|
84
|
+
metadata: skill.value.metadata,
|
|
85
|
+
},
|
|
86
|
+
location: `file://${dir}`,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
const portableSkillDiscoveries = (root) => Effect.gen(function* () {
|
|
90
|
+
const dirs = yield* manifestDirs(root, "SKILL.md");
|
|
91
|
+
const discoveries = yield* Effect.forEach(dirs, readPortableSkillDiscovery, {
|
|
92
|
+
concurrency: FILESYSTEM_DISCOVERY_CONCURRENCY,
|
|
93
|
+
});
|
|
94
|
+
return discoveries.flatMap((discovery) => (Option.isSome(discovery) ? [discovery.value] : []));
|
|
95
|
+
});
|
|
96
|
+
const portableSkillRefsInDir = (source, basePath, options) => Effect.gen(function* () {
|
|
97
|
+
const root = yield* searchRootFor(source, basePath);
|
|
98
|
+
const discoveries = yield* portableSkillDiscoveries(root);
|
|
99
|
+
const matching = discoveries.filter((discovery) => matchesIdentity(discovery, options));
|
|
100
|
+
return yield* Effect.forEach(matching, (discovery) => Effect.gen(function* () {
|
|
101
|
+
const sourcePath = yield* relativeDir(basePath, discovery.location);
|
|
102
|
+
switch (source.type) {
|
|
103
|
+
case "local":
|
|
104
|
+
return {
|
|
105
|
+
type: "skill",
|
|
106
|
+
refType: "local",
|
|
107
|
+
name: discovery.name,
|
|
108
|
+
skill: discovery.skill,
|
|
109
|
+
source,
|
|
110
|
+
sourcePath,
|
|
111
|
+
portable: true,
|
|
112
|
+
location: discovery.location,
|
|
113
|
+
};
|
|
114
|
+
case "github":
|
|
115
|
+
case "gitlab":
|
|
116
|
+
case "bitbucket":
|
|
117
|
+
case "azurerepos":
|
|
118
|
+
case "git":
|
|
119
|
+
return {
|
|
120
|
+
type: "skill",
|
|
121
|
+
refType: "git-hosted",
|
|
122
|
+
name: discovery.name,
|
|
123
|
+
skill: discovery.skill,
|
|
124
|
+
source,
|
|
125
|
+
sourcePath,
|
|
126
|
+
portable: true,
|
|
127
|
+
location: discovery.location,
|
|
128
|
+
gitTreeSha: yield* gitTreeShaFor(source, basePath, discovery.location),
|
|
129
|
+
gitCommitSha: yield* gitCommitShaFor(basePath),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}), { concurrency: GIT_METADATA_CONCURRENCY });
|
|
133
|
+
});
|
|
134
|
+
const skillRefsInDir = (source, basePath, options) => searchRootFor(source, basePath).pipe(Effect.flatMap((root) => discoverSkillPackagesInDir(root, { fullDepth: true })), Effect.flatMap((skills) => Effect.forEach(skills.filter(({ manifest }) => matchesIdentity(manifest, options)), (discovered) => Effect.gen(function* () {
|
|
135
|
+
const skill = {
|
|
136
|
+
name: discovered.manifest.name,
|
|
137
|
+
description: Option.fromUndefinedOr(discovered.manifest.description),
|
|
138
|
+
metadata: Option.none(),
|
|
139
|
+
};
|
|
140
|
+
const identity = {
|
|
141
|
+
owner: discovered.manifest.owner,
|
|
142
|
+
name: discovered.manifest.name,
|
|
143
|
+
};
|
|
144
|
+
switch (source.type) {
|
|
145
|
+
case "local":
|
|
146
|
+
return {
|
|
147
|
+
type: "skill",
|
|
148
|
+
refType: "local",
|
|
149
|
+
skill,
|
|
150
|
+
...identity,
|
|
151
|
+
source,
|
|
152
|
+
location: discovered.location,
|
|
153
|
+
sourcePath: yield* relativeDir(basePath, discovered.location),
|
|
154
|
+
};
|
|
155
|
+
case "github":
|
|
156
|
+
case "gitlab":
|
|
157
|
+
case "bitbucket":
|
|
158
|
+
case "azurerepos":
|
|
159
|
+
case "git":
|
|
160
|
+
return {
|
|
161
|
+
type: "skill",
|
|
162
|
+
refType: "git-hosted",
|
|
163
|
+
skill,
|
|
164
|
+
...identity,
|
|
165
|
+
source,
|
|
166
|
+
location: discovered.location,
|
|
167
|
+
sourcePath: yield* relativeDir(basePath, discovered.location),
|
|
168
|
+
gitTreeSha: yield* gitTreeShaFor(source, basePath, discovered.location),
|
|
169
|
+
gitCommitSha: yield* gitCommitShaFor(basePath),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}), { concurrency: GIT_METADATA_CONCURRENCY })));
|
|
173
|
+
const searchRootFor = (source, basePath) => Effect.gen(function* () {
|
|
174
|
+
const path = yield* Path.Path;
|
|
175
|
+
if (source.type === "local")
|
|
176
|
+
return source.path;
|
|
177
|
+
return Option.match(subPathForSource(source), {
|
|
178
|
+
onNone: () => basePath,
|
|
179
|
+
onSome: (subPath) => path.join(basePath, subPath),
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
const manifestDirs = (root, manifestFilename) => {
|
|
183
|
+
const scan = (dir, depth) => Effect.gen(function* () {
|
|
184
|
+
if (depth > DISCOVERY_MAX_DEPTH)
|
|
185
|
+
return [];
|
|
186
|
+
const fs = yield* FileSystem.FileSystem;
|
|
187
|
+
const path = yield* Path.Path;
|
|
188
|
+
const entries = yield* fs.readDirectory(dir).pipe(Effect.option);
|
|
189
|
+
if (Option.isNone(entries))
|
|
190
|
+
return [];
|
|
191
|
+
const hasManifest = entries.value.includes(manifestFilename);
|
|
192
|
+
const childDirs = yield* Effect.forEach(entries.value, (entry) => Effect.gen(function* () {
|
|
193
|
+
if (DISCOVERY_SKIPPED_DIRECTORIES.has(entry))
|
|
194
|
+
return [];
|
|
195
|
+
const fullPath = path.join(dir, entry);
|
|
196
|
+
const stat = yield* fs.stat(fullPath).pipe(Effect.option);
|
|
197
|
+
if (Option.isNone(stat) || stat.value.type !== "Directory")
|
|
198
|
+
return [];
|
|
199
|
+
return yield* scan(fullPath, depth + 1);
|
|
200
|
+
}), { concurrency: FILESYSTEM_DISCOVERY_CONCURRENCY }).pipe(Effect.map((results) => Array.flatten(results)));
|
|
201
|
+
return hasManifest ? [dir, ...childDirs] : childDirs;
|
|
202
|
+
});
|
|
203
|
+
return scan(root, 0);
|
|
204
|
+
};
|
|
205
|
+
const readSubagentDiscovery = (dir) => Effect.gen(function* () {
|
|
206
|
+
const fs = yield* FileSystem.FileSystem;
|
|
207
|
+
const path = yield* Path.Path;
|
|
208
|
+
const manifestPath = path.join(dir, MANIFEST_FILENAME);
|
|
209
|
+
const raw = yield* fs.readFileString(manifestPath).pipe(Effect.option);
|
|
210
|
+
if (Option.isNone(raw))
|
|
211
|
+
return Option.none();
|
|
212
|
+
const json = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))(raw.value).pipe(Effect.option);
|
|
213
|
+
if (Option.isNone(json))
|
|
214
|
+
return Option.none();
|
|
215
|
+
const manifest = yield* Schema.decodeUnknownEffect(SubagentManifestSchema)(json.value).pipe(Effect.option);
|
|
216
|
+
if (Option.isNone(manifest))
|
|
217
|
+
return Option.none();
|
|
218
|
+
return Option.some({
|
|
219
|
+
type: "subagent",
|
|
220
|
+
owner: manifest.value.owner,
|
|
221
|
+
name: manifest.value.name,
|
|
222
|
+
description: Option.fromUndefinedOr(manifest.value.description),
|
|
223
|
+
location: `file://${dir}`,
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
const readKnowledgeDiscovery = (dir) => Effect.gen(function* () {
|
|
227
|
+
const fs = yield* FileSystem.FileSystem;
|
|
228
|
+
const path = yield* Path.Path;
|
|
229
|
+
const manifestPath = path.join(dir, KNOWLEDGE_MANIFEST_FILENAME);
|
|
230
|
+
const raw = yield* fs.readFileString(manifestPath).pipe(Effect.option);
|
|
231
|
+
if (Option.isNone(raw))
|
|
232
|
+
return Option.none();
|
|
233
|
+
const json = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))(raw.value).pipe(Effect.option);
|
|
234
|
+
if (Option.isNone(json))
|
|
235
|
+
return Option.none();
|
|
236
|
+
const manifest = yield* Schema.decodeUnknownEffect(KnowledgeManifestSchema)(json.value).pipe(Effect.option);
|
|
237
|
+
if (Option.isNone(manifest))
|
|
238
|
+
return Option.none();
|
|
239
|
+
return Option.some({
|
|
240
|
+
type: "knowledge",
|
|
241
|
+
owner: manifest.value.owner,
|
|
242
|
+
name: manifest.value.name,
|
|
243
|
+
location: `file://${dir}`,
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
const subagentDiscoveries = (root) => Effect.gen(function* () {
|
|
247
|
+
const dirs = yield* manifestDirs(root, MANIFEST_FILENAME);
|
|
248
|
+
const discoveries = yield* Effect.forEach(dirs, (dir) => readSubagentDiscovery(dir), {
|
|
249
|
+
concurrency: FILESYSTEM_DISCOVERY_CONCURRENCY,
|
|
250
|
+
});
|
|
251
|
+
return discoveries.flatMap((discovery) => (Option.isSome(discovery) ? [discovery.value] : []));
|
|
252
|
+
});
|
|
253
|
+
const knowledgeDiscoveries = (root) => Effect.gen(function* () {
|
|
254
|
+
const dirs = yield* manifestDirs(root, KNOWLEDGE_MANIFEST_FILENAME);
|
|
255
|
+
const discoveries = yield* Effect.forEach(dirs, (dir) => readKnowledgeDiscovery(dir), {
|
|
256
|
+
concurrency: FILESYSTEM_DISCOVERY_CONCURRENCY,
|
|
257
|
+
});
|
|
258
|
+
return discoveries.flatMap((discovery) => (Option.isSome(discovery) ? [discovery.value] : []));
|
|
259
|
+
});
|
|
260
|
+
const subagentRef = (source, basePath, discovery) => Effect.gen(function* () {
|
|
261
|
+
const subagent = { name: discovery.name, description: discovery.description };
|
|
262
|
+
const identity = { owner: discovery.owner, name: discovery.name };
|
|
263
|
+
switch (source.type) {
|
|
264
|
+
case "local":
|
|
265
|
+
return {
|
|
266
|
+
type: "subagent",
|
|
267
|
+
refType: "local",
|
|
268
|
+
subagent,
|
|
269
|
+
...identity,
|
|
270
|
+
source,
|
|
271
|
+
location: discovery.location,
|
|
272
|
+
sourcePath: yield* relativeDir(basePath, discovery.location),
|
|
273
|
+
};
|
|
274
|
+
case "github":
|
|
275
|
+
case "gitlab":
|
|
276
|
+
case "bitbucket":
|
|
277
|
+
case "azurerepos":
|
|
278
|
+
case "git":
|
|
279
|
+
return {
|
|
280
|
+
type: "subagent",
|
|
281
|
+
refType: "git-hosted",
|
|
282
|
+
subagent,
|
|
283
|
+
...identity,
|
|
284
|
+
source,
|
|
285
|
+
location: discovery.location,
|
|
286
|
+
sourcePath: yield* relativeDir(basePath, discovery.location),
|
|
287
|
+
gitTreeSha: yield* gitTreeShaFor(source, basePath, discovery.location),
|
|
288
|
+
gitCommitSha: yield* gitCommitShaFor(basePath),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
const knowledgeRef = (source, basePath, discovery) => Effect.gen(function* () {
|
|
293
|
+
const knowledge = { name: discovery.name };
|
|
294
|
+
const identity = { owner: discovery.owner, name: discovery.name };
|
|
295
|
+
switch (source.type) {
|
|
296
|
+
case "local":
|
|
297
|
+
return {
|
|
298
|
+
type: "knowledge",
|
|
299
|
+
refType: "local",
|
|
300
|
+
knowledge,
|
|
301
|
+
...identity,
|
|
302
|
+
source,
|
|
303
|
+
location: discovery.location,
|
|
304
|
+
sourcePath: yield* relativeDir(basePath, discovery.location),
|
|
305
|
+
};
|
|
306
|
+
case "github":
|
|
307
|
+
case "gitlab":
|
|
308
|
+
case "bitbucket":
|
|
309
|
+
case "azurerepos":
|
|
310
|
+
case "git":
|
|
311
|
+
return {
|
|
312
|
+
type: "knowledge",
|
|
313
|
+
refType: "git-hosted",
|
|
314
|
+
knowledge,
|
|
315
|
+
...identity,
|
|
316
|
+
source,
|
|
317
|
+
location: discovery.location,
|
|
318
|
+
sourcePath: yield* relativeDir(basePath, discovery.location),
|
|
319
|
+
gitTreeSha: yield* gitTreeShaFor(source, basePath, discovery.location),
|
|
320
|
+
gitCommitSha: yield* gitCommitShaFor(basePath),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
const subagentRefsInDir = (source, basePath, options) => Effect.gen(function* () {
|
|
325
|
+
const root = yield* searchRootFor(source, basePath);
|
|
326
|
+
const discoveries = yield* subagentDiscoveries(root);
|
|
327
|
+
const matching = discoveries.filter((discovery) => matchesIdentity(discovery, options));
|
|
328
|
+
return yield* Effect.forEach(matching, (discovery) => subagentRef(source, basePath, discovery), { concurrency: GIT_METADATA_CONCURRENCY });
|
|
329
|
+
});
|
|
330
|
+
const knowledgeRefsInDir = (source, basePath, options) => Effect.gen(function* () {
|
|
331
|
+
const root = yield* searchRootFor(source, basePath);
|
|
332
|
+
const discoveries = yield* knowledgeDiscoveries(root);
|
|
333
|
+
const matching = discoveries.filter((discovery) => matchesIdentity(discovery, options));
|
|
334
|
+
return yield* Effect.forEach(matching, (discovery) => knowledgeRef(source, basePath, discovery), { concurrency: GIT_METADATA_CONCURRENCY });
|
|
335
|
+
});
|
|
336
|
+
const ruleRefsInDir = (source, basePath, options) => Effect.gen(function* () {
|
|
337
|
+
const root = yield* searchRootFor(source, basePath);
|
|
338
|
+
const discovered = yield* rulePackagesInDir(root, { fullDepth: true });
|
|
339
|
+
const matching = discovered.filter(({ manifest }) => matchesIdentity(manifest, options));
|
|
340
|
+
return yield* Effect.forEach(matching, (discovery) => Effect.gen(function* () {
|
|
341
|
+
const rule = { name: discovery.manifest.name };
|
|
342
|
+
const identity = {
|
|
343
|
+
owner: discovery.manifest.owner,
|
|
344
|
+
name: discovery.manifest.name,
|
|
345
|
+
};
|
|
346
|
+
switch (source.type) {
|
|
347
|
+
case "local":
|
|
348
|
+
return {
|
|
349
|
+
type: "rule",
|
|
350
|
+
refType: "local",
|
|
351
|
+
rule,
|
|
352
|
+
...identity,
|
|
353
|
+
source,
|
|
354
|
+
location: discovery.location,
|
|
355
|
+
sourcePath: yield* relativeDir(basePath, discovery.location),
|
|
356
|
+
};
|
|
357
|
+
case "github":
|
|
358
|
+
case "gitlab":
|
|
359
|
+
case "bitbucket":
|
|
360
|
+
case "azurerepos":
|
|
361
|
+
case "git":
|
|
362
|
+
return {
|
|
363
|
+
type: "rule",
|
|
364
|
+
refType: "git-hosted",
|
|
365
|
+
rule,
|
|
366
|
+
...identity,
|
|
367
|
+
source,
|
|
368
|
+
location: discovery.location,
|
|
369
|
+
sourcePath: yield* relativeDir(basePath, discovery.location),
|
|
370
|
+
gitTreeSha: yield* gitTreeShaFor(source, basePath, discovery.location),
|
|
371
|
+
gitCommitSha: yield* gitCommitShaFor(basePath),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}), { concurrency: GIT_METADATA_CONCURRENCY });
|
|
375
|
+
});
|
|
376
|
+
const hookRefsInDir = (source, basePath, options) => Effect.gen(function* () {
|
|
377
|
+
const root = yield* searchRootFor(source, basePath);
|
|
378
|
+
const discovered = yield* hookPackagesInDir(root, { fullDepth: true });
|
|
379
|
+
const matching = discovered.filter(({ manifest }) => matchesIdentity(manifest, options));
|
|
380
|
+
return yield* Effect.forEach(matching, (discovery) => Effect.gen(function* () {
|
|
381
|
+
const hook = { name: discovery.manifest.name };
|
|
382
|
+
const identity = {
|
|
383
|
+
owner: discovery.manifest.owner,
|
|
384
|
+
name: discovery.manifest.name,
|
|
385
|
+
};
|
|
386
|
+
switch (source.type) {
|
|
387
|
+
case "local":
|
|
388
|
+
return {
|
|
389
|
+
type: "hook",
|
|
390
|
+
refType: "local",
|
|
391
|
+
hook,
|
|
392
|
+
...identity,
|
|
393
|
+
source,
|
|
394
|
+
location: discovery.location,
|
|
395
|
+
sourcePath: yield* relativeDir(basePath, discovery.location),
|
|
396
|
+
};
|
|
397
|
+
case "github":
|
|
398
|
+
case "gitlab":
|
|
399
|
+
case "bitbucket":
|
|
400
|
+
case "azurerepos":
|
|
401
|
+
case "git":
|
|
402
|
+
return {
|
|
403
|
+
type: "hook",
|
|
404
|
+
refType: "git-hosted",
|
|
405
|
+
hook,
|
|
406
|
+
...identity,
|
|
407
|
+
source,
|
|
408
|
+
location: discovery.location,
|
|
409
|
+
sourcePath: yield* relativeDir(basePath, discovery.location),
|
|
410
|
+
gitTreeSha: yield* gitTreeShaFor(source, basePath, discovery.location),
|
|
411
|
+
gitCommitSha: yield* gitCommitShaFor(basePath),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
}), { concurrency: GIT_METADATA_CONCURRENCY });
|
|
415
|
+
});
|
|
416
|
+
const identityKey = (ref) => {
|
|
417
|
+
switch (ref.type) {
|
|
418
|
+
case "subagent":
|
|
419
|
+
return `${ref.type}:${ref.subagent.name}`;
|
|
420
|
+
case "rule":
|
|
421
|
+
return `${ref.type}:${ref.rule.name}`;
|
|
422
|
+
case "hook":
|
|
423
|
+
return `${ref.type}:${ref.hook.name}`;
|
|
424
|
+
case "knowledge":
|
|
425
|
+
return `${ref.type}:${ref.knowledge.name}`;
|
|
426
|
+
case "skill":
|
|
427
|
+
return `${ref.type}:${ref.skill.name}`;
|
|
428
|
+
case "mcp-server":
|
|
429
|
+
return `${ref.type}:${ref.server.name}`;
|
|
430
|
+
case "pack":
|
|
431
|
+
return `${ref.type}:${ref.owner}:${ref.pack.name}`;
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
const rejectAmbiguousDuplicates = (refs) => Effect.gen(function* () {
|
|
435
|
+
const seen = new Set();
|
|
436
|
+
for (const ref of refs) {
|
|
437
|
+
const key = identityKey(ref);
|
|
438
|
+
if (key === undefined)
|
|
439
|
+
continue;
|
|
440
|
+
if (seen.has(key)) {
|
|
441
|
+
return yield* new SourceNotResolvable({
|
|
442
|
+
category: "validation",
|
|
443
|
+
detail: `Multiple discovered extensions declare the same identity: ${key}`,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
seen.add(key);
|
|
447
|
+
}
|
|
448
|
+
return refs;
|
|
449
|
+
});
|
|
450
|
+
export const discoverConventionRefs = (source, basePath, options) => Effect.gen(function* () {
|
|
451
|
+
const refs = [
|
|
452
|
+
...(options.type === "skill" || options.type === "*"
|
|
453
|
+
? [
|
|
454
|
+
...(yield* skillRefsInDir(source, basePath, options)),
|
|
455
|
+
...(yield* portableSkillRefsInDir(source, basePath, options)),
|
|
456
|
+
]
|
|
457
|
+
: []),
|
|
458
|
+
...(options.type === "subagent" || options.type === "*"
|
|
459
|
+
? yield* subagentRefsInDir(source, basePath, options)
|
|
460
|
+
: []),
|
|
461
|
+
...(options.type === "rule" || options.type === "*"
|
|
462
|
+
? yield* ruleRefsInDir(source, basePath, options)
|
|
463
|
+
: []),
|
|
464
|
+
...(options.type === "hook" || options.type === "*"
|
|
465
|
+
? yield* hookRefsInDir(source, basePath, options)
|
|
466
|
+
: []),
|
|
467
|
+
...(options.type === "knowledge" || options.type === "*"
|
|
468
|
+
? yield* knowledgeRefsInDir(source, basePath, options)
|
|
469
|
+
: []),
|
|
470
|
+
];
|
|
471
|
+
return yield* rejectAmbiguousDuplicates(refs);
|
|
472
|
+
});
|
|
473
|
+
//# sourceMappingURL=convention-discovery.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared source provider factory for git hosting platforms (GitHub, GitLab, Bitbucket).
|
|
3
|
+
*
|
|
4
|
+
* Wraps existing clone + discover logic into the `SourceHostProvider` interface.
|
|
5
|
+
*
|
|
6
|
+
* @experimental This API is unstable and may change without notice.
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import * as FileSystem from "effect/FileSystem";
|
|
10
|
+
import * as Path from "effect/Path";
|
|
11
|
+
import type * as Scope from "effect/Scope";
|
|
12
|
+
import { type SourceError } from "../errors.js";
|
|
13
|
+
import type { SourceHostProvider } from "@agentxm/extension-model/unstable/sources/source-host-provider";
|
|
14
|
+
import type { GitHubSource, GitLabSource, BitbucketSource, AzureReposSource, GitHostingSourceHost } from "@agentxm/extension-model/unstable/sources/types";
|
|
15
|
+
/**
|
|
16
|
+
* Creates a `SourceHostProvider` for a git hosting platform.
|
|
17
|
+
*
|
|
18
|
+
* Constructed with a `SourceHost` that provides the host URL.
|
|
19
|
+
* The `match` method checks if a URL's hostname matches the configured host.
|
|
20
|
+
* The `find` implementation clones the repository into a scoped temp directory,
|
|
21
|
+
* discovers skills via the 3-phase algorithm, and enriches each with its git
|
|
22
|
+
* tree SHA.
|
|
23
|
+
*
|
|
24
|
+
* @experimental This API is unstable and may change without notice.
|
|
25
|
+
*/
|
|
26
|
+
export declare const createGitHostingSourceHostProvider: <S extends GitHubSource | GitLabSource | BitbucketSource | AzureReposSource>(host: Extract<GitHostingSourceHost, {
|
|
27
|
+
type: S["type"];
|
|
28
|
+
}>) => SourceHostProvider<S, FileSystem.FileSystem | Path.Path | Scope.Scope, SourceError>;
|
|
29
|
+
/** Build a clone URL from a git hosting source (internal helper). */
|
|
30
|
+
export declare const buildCloneUrlForSource: (source: GitHubSource | GitLabSource | BitbucketSource | AzureReposSource) => string;
|
|
31
|
+
//# sourceMappingURL=git-hosting.d.ts.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared source provider factory for git hosting platforms (GitHub, GitLab, Bitbucket).
|
|
3
|
+
*
|
|
4
|
+
* Wraps existing clone + discover logic into the `SourceHostProvider` interface.
|
|
5
|
+
*
|
|
6
|
+
* @experimental This API is unstable and may change without notice.
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import * as FileSystem from "effect/FileSystem";
|
|
10
|
+
import * as Path from "effect/Path";
|
|
11
|
+
import * as Effect from "effect/Effect";
|
|
12
|
+
import * as Option from "effect/Option";
|
|
13
|
+
import { SourceNetworkFailure } from "../errors.js";
|
|
14
|
+
import { shallowClone } from "../git/operations.js";
|
|
15
|
+
import { fileUrlToPath } from "../file-url.js";
|
|
16
|
+
import { discoverConventionRefs } from "./convention-discovery.js";
|
|
17
|
+
// -----------------------------------------------------------------------------
|
|
18
|
+
// New Factory (SourceHostProvider)
|
|
19
|
+
// -----------------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* Creates a `SourceHostProvider` for a git hosting platform.
|
|
22
|
+
*
|
|
23
|
+
* Constructed with a `SourceHost` that provides the host URL.
|
|
24
|
+
* The `match` method checks if a URL's hostname matches the configured host.
|
|
25
|
+
* The `find` implementation clones the repository into a scoped temp directory,
|
|
26
|
+
* discovers skills via the 3-phase algorithm, and enriches each with its git
|
|
27
|
+
* tree SHA.
|
|
28
|
+
*
|
|
29
|
+
* @experimental This API is unstable and may change without notice.
|
|
30
|
+
*/
|
|
31
|
+
export const createGitHostingSourceHostProvider = (host) => ({
|
|
32
|
+
type: host.type,
|
|
33
|
+
match: (url) => Effect.succeed(url.hostname === host.url.hostname),
|
|
34
|
+
find: (source, options) => Effect.gen(function* () {
|
|
35
|
+
const fs = yield* FileSystem.FileSystem;
|
|
36
|
+
const cloneUrl = buildCloneUrlForSource(source);
|
|
37
|
+
// Acquire scoped temp directory (cleaned up when the scope closes)
|
|
38
|
+
const tempDir = yield* Effect.acquireRelease(fs.makeTempDirectory({ prefix: "axm-source-discovery-" }).pipe(Effect.mapError((error) => new SourceNetworkFailure({
|
|
39
|
+
detail: "Temporary source directory could not be created",
|
|
40
|
+
cause: error,
|
|
41
|
+
}))), (dir) => fs.remove(dir, { recursive: true }).pipe(Effect.ignore));
|
|
42
|
+
yield* shallowClone(cloneUrl, tempDir, Option.getOrUndefined(source.ref));
|
|
43
|
+
return yield* discoverConventionRefs(source, tempDir, options);
|
|
44
|
+
}),
|
|
45
|
+
fetch: (_source, _ref) => {
|
|
46
|
+
if (_ref.refType !== "git-hosted") {
|
|
47
|
+
return Effect.fail(new SourceNetworkFailure({
|
|
48
|
+
detail: `Expected ref with location for ${host.type} source, but none was provided`,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
return Effect.succeed({
|
|
52
|
+
directory: fileUrlToPath(_ref.location),
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
/** Build a clone URL from a git hosting source (internal helper). */
|
|
57
|
+
export const buildCloneUrlForSource = (source) => {
|
|
58
|
+
const explicitCloneUrl = Option.getOrUndefined(source.cloneUrl ?? Option.none());
|
|
59
|
+
if (explicitCloneUrl !== undefined) {
|
|
60
|
+
return explicitCloneUrl;
|
|
61
|
+
}
|
|
62
|
+
switch (source.type) {
|
|
63
|
+
case "github":
|
|
64
|
+
return `${source.url.origin}/${source.owner}/${source.repo}.git`;
|
|
65
|
+
case "gitlab":
|
|
66
|
+
return `${source.url.origin}/${source.owner}/${source.repo}.git`;
|
|
67
|
+
case "bitbucket":
|
|
68
|
+
return `${source.url.origin}/${source.owner}/${source.repo}.git`;
|
|
69
|
+
case "azurerepos":
|
|
70
|
+
return `${source.url.origin}/${source.organization}/${source.project}/_git/${source.repo}`;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=git-hosting.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source provider for generic git repositories.
|
|
3
|
+
*
|
|
4
|
+
* @experimental This API is unstable and may change without notice.
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
import * as FileSystem from "effect/FileSystem";
|
|
8
|
+
import * as Path from "effect/Path";
|
|
9
|
+
import type * as Scope from "effect/Scope";
|
|
10
|
+
import { type SourceError } from "../errors.js";
|
|
11
|
+
import type { SourceHostProvider } from "@agentxm/extension-model/unstable/sources/source-host-provider";
|
|
12
|
+
import type { GitSource } from "@agentxm/extension-model/unstable/sources/types";
|
|
13
|
+
/**
|
|
14
|
+
* Source host provider for generic git URLs.
|
|
15
|
+
*
|
|
16
|
+
* Self-describing — no host config needed.
|
|
17
|
+
* `match` returns true for git://, ssh://, and git@... URL schemes.
|
|
18
|
+
*
|
|
19
|
+
* @experimental This API is unstable and may change without notice.
|
|
20
|
+
*/
|
|
21
|
+
export declare const createGitSourceHostProvider: () => SourceHostProvider<GitSource, FileSystem.FileSystem | Path.Path | Scope.Scope, SourceError>;
|
|
22
|
+
//# sourceMappingURL=git.d.ts.map
|