@agentxm/extension-discovery 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.
Files changed (72) hide show
  1. package/LICENSE +110 -0
  2. package/dist/src/discover.d.ts +34 -0
  3. package/dist/src/discover.js +133 -0
  4. package/dist/src/index.d.ts +13 -0
  5. package/dist/src/index.js +12 -0
  6. package/dist/src/internal/environment.d.ts +14 -0
  7. package/dist/src/internal/environment.js +15 -0
  8. package/dist/src/packaging/bazel.d.ts +27 -0
  9. package/dist/src/packaging/bazel.js +125 -0
  10. package/dist/src/packaging/cargo.d.ts +32 -0
  11. package/dist/src/packaging/cargo.js +270 -0
  12. package/dist/src/packaging/cocoapods.d.ts +26 -0
  13. package/dist/src/packaging/cocoapods.js +187 -0
  14. package/dist/src/packaging/composer.d.ts +26 -0
  15. package/dist/src/packaging/composer.js +144 -0
  16. package/dist/src/packaging/conan.d.ts +30 -0
  17. package/dist/src/packaging/conan.js +238 -0
  18. package/dist/src/packaging/conda.d.ts +26 -0
  19. package/dist/src/packaging/conda.js +320 -0
  20. package/dist/src/packaging/cpan.d.ts +29 -0
  21. package/dist/src/packaging/cpan.js +189 -0
  22. package/dist/src/packaging/cran.d.ts +29 -0
  23. package/dist/src/packaging/cran.js +176 -0
  24. package/dist/src/packaging/detect.d.ts +16 -0
  25. package/dist/src/packaging/detect.js +25 -0
  26. package/dist/src/packaging/detected-package.d.ts +14 -0
  27. package/dist/src/packaging/detected-package.js +20 -0
  28. package/dist/src/packaging/docker.d.ts +27 -0
  29. package/dist/src/packaging/docker.js +256 -0
  30. package/dist/src/packaging/gem.d.ts +26 -0
  31. package/dist/src/packaging/gem.js +234 -0
  32. package/dist/src/packaging/golang.d.ts +26 -0
  33. package/dist/src/packaging/golang.js +169 -0
  34. package/dist/src/packaging/hackage.d.ts +26 -0
  35. package/dist/src/packaging/hackage.js +305 -0
  36. package/dist/src/packaging/hex.d.ts +28 -0
  37. package/dist/src/packaging/hex.js +186 -0
  38. package/dist/src/packaging/huggingface.d.ts +20 -0
  39. package/dist/src/packaging/huggingface.js +112 -0
  40. package/dist/src/packaging/index.d.ts +44 -0
  41. package/dist/src/packaging/index.js +120 -0
  42. package/dist/src/packaging/jsr.d.ts +30 -0
  43. package/dist/src/packaging/jsr.js +227 -0
  44. package/dist/src/packaging/julia.d.ts +27 -0
  45. package/dist/src/packaging/julia.js +165 -0
  46. package/dist/src/packaging/luarocks.d.ts +28 -0
  47. package/dist/src/packaging/luarocks.js +159 -0
  48. package/dist/src/packaging/maven.d.ts +27 -0
  49. package/dist/src/packaging/maven.js +471 -0
  50. package/dist/src/packaging/mojo.d.ts +29 -0
  51. package/dist/src/packaging/mojo.js +148 -0
  52. package/dist/src/packaging/npm.d.ts +26 -0
  53. package/dist/src/packaging/npm.js +190 -0
  54. package/dist/src/packaging/nuget.d.ts +26 -0
  55. package/dist/src/packaging/nuget.js +240 -0
  56. package/dist/src/packaging/opam.d.ts +27 -0
  57. package/dist/src/packaging/opam.js +287 -0
  58. package/dist/src/packaging/pub.d.ts +26 -0
  59. package/dist/src/packaging/pub.js +357 -0
  60. package/dist/src/packaging/pypi.d.ts +23 -0
  61. package/dist/src/packaging/pypi.js +448 -0
  62. package/dist/src/packaging/read.d.ts +20 -0
  63. package/dist/src/packaging/read.js +31 -0
  64. package/dist/src/packaging/reader-io.d.ts +29 -0
  65. package/dist/src/packaging/reader-io.js +29 -0
  66. package/dist/src/packaging/swift.d.ts +26 -0
  67. package/dist/src/packaging/swift.js +141 -0
  68. package/dist/src/packaging/types.d.ts +43 -0
  69. package/dist/src/packaging/types.js +8 -0
  70. package/dist/src/packaging/zig.d.ts +32 -0
  71. package/dist/src/packaging/zig.js +151 -0
  72. package/package.json +54 -0
@@ -0,0 +1,471 @@
1
+ /**
2
+ * Maven/Gradle package detector and reader for package-compatibility discovery.
3
+ *
4
+ * @experimental This API is unstable and may change without notice.
5
+ * @packageDocumentation
6
+ */
7
+ // Intentional escape hatch: node:os homedir() has no @effect/platform equivalent.
8
+ import * as os from "node:os";
9
+ import * as Effect from "effect/Effect";
10
+ import * as FileSystem from "effect/FileSystem";
11
+ import * as Option from "effect/Option";
12
+ import * as Path from "effect/Path";
13
+ import * as Result from "effect/Result";
14
+ import * as Schema from "effect/Schema";
15
+ import { parseTomlStringEntries, readTomlSection } from "@agentxm/extension-workspace";
16
+ import { makeDetectedPackage } from "./detected-package.js";
17
+ import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
18
+ import { decodeAxmMeta, parseJsonOptional, readFileOptional } from "./reader-io.js";
19
+ const mavenType = Schema.decodeUnknownSync(PackageTypeSchema)("maven");
20
+ /**
21
+ * Returns true if the version string is a Maven version range.
22
+ * Ranges use brackets/parens like [1.0,2.0), (,1.0], etc.
23
+ */
24
+ const isMavenVersionRange = (version) => /^[[(]/.test(version) || /[)\]]$/.test(version);
25
+ /**
26
+ * Returns true if the version string is a property reference like ${foo.bar}.
27
+ */
28
+ const isPropertyReference = (version) => /^\$\{.+\}$/.test(version);
29
+ /**
30
+ * Extract the property name from a property reference like ${foo.bar} -> foo.bar.
31
+ */
32
+ const extractPropertyName = (version) => {
33
+ const match = /^\$\{(.+)\}$/.exec(version);
34
+ return match?.[1];
35
+ };
36
+ /**
37
+ * Parse <properties> from pom.xml content. Returns a map of property name to value.
38
+ */
39
+ const parseProperties = (content) => {
40
+ const props = new Map();
41
+ const propsBlockMatch = /<properties>([\s\S]*?)<\/properties>/i.exec(content);
42
+ if (propsBlockMatch?.[1] === undefined)
43
+ return props;
44
+ const propRegex = /<([a-zA-Z0-9_.:-]+)>\s*(.*?)\s*<\/\1>/g;
45
+ let match;
46
+ while ((match = propRegex.exec(propsBlockMatch[1])) !== null) {
47
+ if (match[1] !== undefined && match[2] !== undefined) {
48
+ props.set(match[1], match[2]);
49
+ }
50
+ }
51
+ return props;
52
+ };
53
+ /**
54
+ * Resolve a version string, handling property references.
55
+ */
56
+ const resolveVersion = (version, properties) => {
57
+ if (version === undefined)
58
+ return undefined;
59
+ if (isMavenVersionRange(version))
60
+ return undefined;
61
+ if (isPropertyReference(version)) {
62
+ const propName = extractPropertyName(version);
63
+ if (propName === undefined)
64
+ return undefined;
65
+ const resolved = properties.get(propName);
66
+ if (resolved === undefined)
67
+ return undefined;
68
+ // Recursively resolve in case the property itself references another
69
+ return resolveVersion(resolved, properties);
70
+ }
71
+ return version;
72
+ };
73
+ /**
74
+ * Create a DetectedPackage from groupId, artifactId, and optional version.
75
+ */
76
+ const makeMavenPackage = (groupId, artifactId, version, source) => Option.getOrUndefined(makeDetectedPackage({
77
+ type: mavenType,
78
+ ...(groupId === "" ? {} : { namespace: groupId }),
79
+ name: artifactId,
80
+ ...(version === undefined ? {} : { version }),
81
+ source,
82
+ }));
83
+ const appendMavenPackage = (results, groupId, artifactId, version, source) => {
84
+ const detected = makeMavenPackage(groupId, artifactId, version, source);
85
+ if (detected !== undefined)
86
+ results.push(detected);
87
+ };
88
+ /**
89
+ * Parse pom.xml content and extract dependencies.
90
+ */
91
+ const parsePomXml = (content, source) => {
92
+ const properties = parseProperties(content);
93
+ const results = [];
94
+ // Match <dependency> blocks
95
+ const depRegex = /<dependency>([\s\S]*?)<\/dependency>/gi;
96
+ let depMatch;
97
+ while ((depMatch = depRegex.exec(content)) !== null) {
98
+ const block = depMatch[1];
99
+ if (block === undefined)
100
+ continue;
101
+ const groupIdMatch = /<groupId>\s*(.*?)\s*<\/groupId>/i.exec(block);
102
+ const artifactIdMatch = /<artifactId>\s*(.*?)\s*<\/artifactId>/i.exec(block);
103
+ const versionMatch = /<version>\s*(.*?)\s*<\/version>/i.exec(block);
104
+ const groupId = groupIdMatch?.[1];
105
+ const artifactId = artifactIdMatch?.[1];
106
+ if (groupId === undefined || artifactId === undefined)
107
+ continue;
108
+ const rawVersion = versionMatch?.[1];
109
+ const version = resolveVersion(rawVersion, properties);
110
+ appendMavenPackage(results, groupId, artifactId, version, source);
111
+ }
112
+ return results;
113
+ };
114
+ /** Gradle configuration keywords that declare dependencies. */
115
+ const GRADLE_CONFIGS = [
116
+ "implementation",
117
+ "api",
118
+ "compileOnly",
119
+ "runtimeOnly",
120
+ "testImplementation",
121
+ "testCompileOnly",
122
+ "testRuntimeOnly",
123
+ "annotationProcessor",
124
+ "kapt",
125
+ ];
126
+ /**
127
+ * Parse a Gradle dependency string like "group:name:version" or "group:name".
128
+ */
129
+ const parseGradleCoordinate = (coordinate, source) => {
130
+ const parts = coordinate.split(":");
131
+ if (parts.length < 2)
132
+ return undefined;
133
+ const groupId = parts[0];
134
+ const artifactId = parts[1];
135
+ if (groupId === undefined || artifactId === undefined)
136
+ return undefined;
137
+ if (groupId.trim() === "" || artifactId.trim() === "")
138
+ return undefined;
139
+ const version = parts.length >= 3 && parts[2] !== undefined && parts[2].trim() !== ""
140
+ ? parts[2].trim()
141
+ : undefined;
142
+ return makeMavenPackage(groupId.trim(), artifactId.trim(), version, source);
143
+ };
144
+ /**
145
+ * Parse build.gradle or build.gradle.kts content and extract dependencies.
146
+ */
147
+ const parseGradleBuild = (content, source) => {
148
+ const results = [];
149
+ const configPattern = GRADLE_CONFIGS.join("|");
150
+ // Groovy DSL: implementation 'group:name:version'
151
+ const groovyRegex = new RegExp(`(?:${configPattern})\\s+['"]([^'"]+)['"]`, "g");
152
+ let match;
153
+ while ((match = groovyRegex.exec(content)) !== null) {
154
+ if (match[1] === undefined)
155
+ continue;
156
+ const detected = parseGradleCoordinate(match[1], source);
157
+ if (detected !== undefined)
158
+ results.push(detected);
159
+ }
160
+ // Kotlin DSL: implementation("group:name:version")
161
+ const kotlinRegex = new RegExp(`(?:${configPattern})\\(\\s*['"]([^'"]+)['"]\\s*\\)`, "g");
162
+ while ((match = kotlinRegex.exec(content)) !== null) {
163
+ if (match[1] === undefined)
164
+ continue;
165
+ const detected = parseGradleCoordinate(match[1], source);
166
+ if (detected !== undefined)
167
+ results.push(detected);
168
+ }
169
+ return results;
170
+ };
171
+ /**
172
+ * Parse gradle/libs.versions.toml content and extract library entries.
173
+ * Supports `module = "group:name"` and separate `group`/`name` keys.
174
+ */
175
+ const parseVersionCatalog = (content, source) => {
176
+ const results = [];
177
+ const librariesSection = readTomlSection(content, "libraries");
178
+ if (librariesSection === undefined)
179
+ return results;
180
+ // Parse [versions] section for version references
181
+ const versions = new Map();
182
+ const versionsSection = readTomlSection(content, "versions");
183
+ if (versionsSection !== undefined) {
184
+ for (const { key, value } of parseTomlStringEntries(versionsSection)) {
185
+ versions.set(key, value);
186
+ }
187
+ }
188
+ // Parse library entries line by line
189
+ const lines = librariesSection.split("\n");
190
+ for (const line of lines) {
191
+ const trimmed = line.trim();
192
+ if (trimmed === "" || trimmed.startsWith("#"))
193
+ continue;
194
+ // Match: name = { ... }
195
+ const entryMatch = /^[a-zA-Z0-9_-]+\s*=\s*\{(.+)\}/.exec(trimmed);
196
+ if (entryMatch?.[1] === undefined)
197
+ continue;
198
+ const props = entryMatch[1];
199
+ let groupId;
200
+ let artifactId;
201
+ let version;
202
+ // Check for module = "group:name"
203
+ const moduleMatch = /module\s*=\s*"([^"]+)"/.exec(props);
204
+ if (moduleMatch?.[1] !== undefined) {
205
+ const parts = moduleMatch[1].split(":");
206
+ groupId = parts[0];
207
+ artifactId = parts[1];
208
+ }
209
+ else {
210
+ // Check for separate group and name keys
211
+ const groupMatch = /group\s*=\s*"([^"]+)"/.exec(props);
212
+ const nameMatch = /name\s*=\s*"([^"]+)"/.exec(props);
213
+ groupId = groupMatch?.[1];
214
+ artifactId = nameMatch?.[1];
215
+ }
216
+ if (groupId === undefined || artifactId === undefined)
217
+ continue;
218
+ // Check for version
219
+ const versionMatch = /version\s*=\s*"([^"]+)"/.exec(props);
220
+ const versionRefMatch = /version\.ref\s*=\s*"([^"]+)"/.exec(props);
221
+ if (versionMatch?.[1] !== undefined) {
222
+ version = versionMatch[1];
223
+ }
224
+ else if (versionRefMatch?.[1] !== undefined) {
225
+ version = versions.get(versionRefMatch[1]);
226
+ }
227
+ appendMavenPackage(results, groupId, artifactId, version, source);
228
+ }
229
+ return results;
230
+ };
231
+ /**
232
+ * Parse deps.edn content and extract Maven dependencies.
233
+ *
234
+ * Supports dependencies declared in root `:deps` and alias `:extra-deps` maps.
235
+ */
236
+ const parseDepsEdn = (content, source) => {
237
+ const results = [];
238
+ const withoutComments = content.replace(/;[^\n\r]*/g, "");
239
+ const depRegex = /(^|[\s{])([A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)?)\s*\{[^}]*:mvn\/version\s+"([^"]+)"[^}]*}/g;
240
+ let match;
241
+ while ((match = depRegex.exec(withoutComments)) !== null) {
242
+ const coordinate = match[2];
243
+ const version = match[3];
244
+ if (coordinate === undefined || version === undefined)
245
+ continue;
246
+ const parts = coordinate.split("/");
247
+ if (parts.length === 1) {
248
+ const artifactId = parts[0];
249
+ if (artifactId !== undefined && artifactId.trim() !== "") {
250
+ appendMavenPackage(results, "", artifactId.trim(), version, source);
251
+ }
252
+ continue;
253
+ }
254
+ if (parts.length === 2) {
255
+ const groupId = parts[0];
256
+ const artifactId = parts[1];
257
+ if (groupId !== undefined &&
258
+ artifactId !== undefined &&
259
+ groupId.trim() !== "" &&
260
+ artifactId.trim() !== "") {
261
+ appendMavenPackage(results, groupId.trim(), artifactId.trim(), version, source);
262
+ }
263
+ }
264
+ }
265
+ return results;
266
+ };
267
+ /**
268
+ * Deduplicate packages by purl identity (namespace + name).
269
+ */
270
+ const deduplicatePackages = (packages) => {
271
+ const seen = new Set();
272
+ const results = [];
273
+ for (const pkg of packages) {
274
+ const key = `${pkg.purl.namespace ?? ""}/${pkg.purl.name}`;
275
+ if (!seen.has(key)) {
276
+ seen.add(key);
277
+ results.push(pkg);
278
+ }
279
+ }
280
+ return results;
281
+ };
282
+ /**
283
+ * List files matching a glob pattern in a directory.
284
+ */
285
+ const listDirectory = (dirPath) => Effect.gen(function* () {
286
+ const fs = yield* FileSystem.FileSystem;
287
+ const entries = yield* fs.readDirectory(dirPath).pipe(Effect.option);
288
+ return entries;
289
+ });
290
+ /**
291
+ * Find JAR files in a directory.
292
+ */
293
+ const findJarsInDir = (dirPath) => Effect.gen(function* () {
294
+ const entries = yield* listDirectory(dirPath);
295
+ if (Option.isNone(entries))
296
+ return [];
297
+ return entries.value.filter((e) => e.endsWith(".jar"));
298
+ });
299
+ /**
300
+ * Read a ZIP file entry using the built-in Bun/Node zip APIs.
301
+ * Returns Option.none if the entry is not found.
302
+ */
303
+ const readZipEntry = (zipPath, entryName) => Effect.gen(function* () {
304
+ const fs = yield* FileSystem.FileSystem;
305
+ // Read the jar file as bytes
306
+ const bytes = yield* fs.readFile(zipPath).pipe(Effect.option);
307
+ if (Option.isNone(bytes))
308
+ return Option.none();
309
+ // Use simple ZIP parsing to find the entry
310
+ // ZIP files have a central directory at the end
311
+ const data = bytes.value;
312
+ const content = yield* Effect.try({
313
+ try: () => {
314
+ // Search for the local file header matching our entry name
315
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
316
+ // Scan for local file headers (PK\x03\x04)
317
+ for (let i = 0; i < data.length - 30; i++) {
318
+ if (data[i] === 0x50 &&
319
+ data[i + 1] === 0x4b &&
320
+ data[i + 2] === 0x03 &&
321
+ data[i + 3] === 0x04) {
322
+ const nameLength = view.getUint16(i + 26, true);
323
+ const extraLength = view.getUint16(i + 28, true);
324
+ const compressedSize = view.getUint32(i + 18, true);
325
+ const compressionMethod = view.getUint16(i + 8, true);
326
+ // Check if filename matches
327
+ const fileNameStart = i + 30;
328
+ const fileName = new TextDecoder().decode(data.slice(fileNameStart, fileNameStart + nameLength));
329
+ if (fileName === entryName) {
330
+ // Only handle STORED (0) entries for simplicity
331
+ if (compressionMethod !== 0) {
332
+ return undefined;
333
+ }
334
+ const dataStart = fileNameStart + nameLength + extraLength;
335
+ const entryData = data.slice(dataStart, dataStart + compressedSize);
336
+ return new TextDecoder().decode(entryData);
337
+ }
338
+ }
339
+ }
340
+ return undefined;
341
+ },
342
+ catch: () => ({ _tag: "ZipReadError" }),
343
+ }).pipe(Effect.option);
344
+ if (Option.isNone(content) || content.value === undefined) {
345
+ return Option.none();
346
+ }
347
+ return Option.some(content.value);
348
+ });
349
+ /**
350
+ * Maven package detector.
351
+ *
352
+ * Scans `pom.xml`, `build.gradle`, `build.gradle.kts`, and
353
+ * `gradle/libs.versions.toml` in the project directory. Also detects Clojure
354
+ * `deps.edn` Maven coordinates declared with `:mvn/version`.
355
+ *
356
+ * @experimental This API is unstable and may change without notice.
357
+ */
358
+ export const mavenDetector = {
359
+ type: mavenType,
360
+ detect: Effect.fn("detect.maven")(function* (projectDir) {
361
+ const path = yield* Path.Path;
362
+ const allPackages = [];
363
+ // Parse pom.xml
364
+ const pomPath = path.join(projectDir, "pom.xml");
365
+ const pomContent = yield* readFileOptional(pomPath);
366
+ if (Option.isSome(pomContent)) {
367
+ // Basic validation: check for XML-like content
368
+ if (pomContent.value.includes("<") && pomContent.value.includes(">")) {
369
+ const pomDeps = parsePomXml(pomContent.value, pomPath);
370
+ allPackages.push(...pomDeps);
371
+ }
372
+ else {
373
+ yield* Effect.logWarning("Malformed pom.xml: not valid XML, skipping");
374
+ }
375
+ }
376
+ // Parse build.gradle
377
+ const gradlePath = path.join(projectDir, "build.gradle");
378
+ const gradleContent = yield* readFileOptional(gradlePath);
379
+ if (Option.isSome(gradleContent)) {
380
+ const gradleDeps = parseGradleBuild(gradleContent.value, gradlePath);
381
+ allPackages.push(...gradleDeps);
382
+ }
383
+ // Parse build.gradle.kts
384
+ const gradleKtsPath = path.join(projectDir, "build.gradle.kts");
385
+ const gradleKtsContent = yield* readFileOptional(gradleKtsPath);
386
+ if (Option.isSome(gradleKtsContent)) {
387
+ const gradleKtsDeps = parseGradleBuild(gradleKtsContent.value, gradleKtsPath);
388
+ allPackages.push(...gradleKtsDeps);
389
+ }
390
+ // Parse gradle/libs.versions.toml
391
+ const versionCatalogPath = path.join(projectDir, "gradle", "libs.versions.toml");
392
+ const catalogContent = yield* readFileOptional(versionCatalogPath);
393
+ if (Option.isSome(catalogContent)) {
394
+ const catalogDeps = parseVersionCatalog(catalogContent.value, versionCatalogPath);
395
+ allPackages.push(...catalogDeps);
396
+ }
397
+ // Parse deps.edn
398
+ const depsEdnPath = path.join(projectDir, "deps.edn");
399
+ const depsEdnContent = yield* readFileOptional(depsEdnPath);
400
+ if (Option.isSome(depsEdnContent)) {
401
+ const depsEdnDeps = parseDepsEdn(depsEdnContent.value, depsEdnPath);
402
+ allPackages.push(...depsEdnDeps);
403
+ }
404
+ return deduplicatePackages(allPackages);
405
+ }, Effect.annotateLogs({ detector: "maven" }), Effect.withSpan("detect.maven")),
406
+ };
407
+ /**
408
+ * Maven package reader.
409
+ *
410
+ * Reads `META-INF/axm.json` from local JAR files in `~/.m2/repository/`
411
+ * or Gradle cache `~/.gradle/caches/modules-2/files-2.1/`.
412
+ *
413
+ * @experimental This API is unstable and may change without notice.
414
+ */
415
+ export const mavenReader = {
416
+ type: mavenType,
417
+ read: Effect.fn("read.maven")(function* (pkg) {
418
+ const path = yield* Path.Path;
419
+ const home = os.homedir();
420
+ const groupId = pkg.purl.namespace ?? "";
421
+ const artifactId = pkg.purl.name;
422
+ const version = pkg.purl.version ?? "0.0.0";
423
+ // Convert groupId dots to path separators for Maven repo
424
+ const groupIdPath = groupId.replace(/\./g, "/");
425
+ // Try Maven local repository first
426
+ const m2Dir = path.join(home, ".m2", "repository", groupIdPath, artifactId, version);
427
+ const m2Jars = yield* findJarsInDir(m2Dir);
428
+ for (const jarName of m2Jars) {
429
+ const jarPath = path.join(m2Dir, jarName);
430
+ const axmContent = yield* readZipEntry(jarPath, "META-INF/axm.json");
431
+ if (Option.isSome(axmContent)) {
432
+ const parsed = yield* parseJsonOptional(axmContent.value, `${groupId}:${artifactId}@${version}/META-INF/axm.json`);
433
+ if (Option.isSome(parsed)) {
434
+ const metaResult = decodeAxmMeta(parsed.value);
435
+ if (Result.isFailure(metaResult)) {
436
+ yield* Effect.logWarning(`Invalid axm metadata in ${groupId}:${artifactId}@${version}: schema validation failed`);
437
+ return Option.none();
438
+ }
439
+ return Option.some(metaResult.success.extensions);
440
+ }
441
+ }
442
+ }
443
+ // Try Gradle cache
444
+ const gradleCacheDir = path.join(home, ".gradle", "caches", "modules-2", "files-2.1", groupId, artifactId, version);
445
+ // Gradle cache has hash subdirectories
446
+ const gradleHashDirs = yield* listDirectory(gradleCacheDir);
447
+ if (Option.isSome(gradleHashDirs)) {
448
+ for (const hashDir of gradleHashDirs.value) {
449
+ const hashDirPath = path.join(gradleCacheDir, hashDir);
450
+ const jars = yield* findJarsInDir(hashDirPath);
451
+ for (const jarName of jars) {
452
+ const jarPath = path.join(hashDirPath, jarName);
453
+ const axmContent = yield* readZipEntry(jarPath, "META-INF/axm.json");
454
+ if (Option.isSome(axmContent)) {
455
+ const parsed = yield* parseJsonOptional(axmContent.value, `${groupId}:${artifactId}@${version}/META-INF/axm.json`);
456
+ if (Option.isSome(parsed)) {
457
+ const metaResult = decodeAxmMeta(parsed.value);
458
+ if (Result.isFailure(metaResult)) {
459
+ yield* Effect.logWarning(`Invalid axm metadata in ${groupId}:${artifactId}@${version}: schema validation failed`);
460
+ return Option.none();
461
+ }
462
+ return Option.some(metaResult.success.extensions);
463
+ }
464
+ }
465
+ }
466
+ }
467
+ }
468
+ return Option.none();
469
+ }, Effect.annotateLogs({ reader: "maven" }), Effect.withSpan("read.maven")),
470
+ };
471
+ //# sourceMappingURL=maven.js.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Mojo package detector and reader for package-compatibility discovery.
3
+ *
4
+ * Parses `pixi.toml` for dependencies and reads
5
+ * axm metadata from pixi environment cache at `.pixi/envs/`.
6
+ *
7
+ * @experimental This API is unstable and may change without notice.
8
+ * @packageDocumentation
9
+ */
10
+ import type { PackageDetector, PackageReader } from "./types.js";
11
+ /**
12
+ * Mojo package detector.
13
+ *
14
+ * Scans `pixi.toml` in the project directory. Mojo-specific packages produce `pkg:mojo`
15
+ * purls, while conda packages produce `pkg:conda` purls.
16
+ *
17
+ * @experimental This API is unstable and may change without notice.
18
+ */
19
+ export declare const mojoDetector: PackageDetector;
20
+ /**
21
+ * Mojo package reader.
22
+ *
23
+ * Reads `axm.json` sidecar files from `.pixi/envs/<env>/conda-meta/`
24
+ * in the project directory for each detected Mojo/conda package.
25
+ *
26
+ * @experimental This API is unstable and may change without notice.
27
+ */
28
+ export declare const mojoReader: PackageReader;
29
+ //# sourceMappingURL=mojo.d.ts.map
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Mojo package detector and reader for package-compatibility discovery.
3
+ *
4
+ * Parses `pixi.toml` for dependencies and reads
5
+ * axm metadata from pixi environment cache at `.pixi/envs/`.
6
+ *
7
+ * @experimental This API is unstable and may change without notice.
8
+ * @packageDocumentation
9
+ */
10
+ import * as Effect from "effect/Effect";
11
+ import * as FileSystem from "effect/FileSystem";
12
+ import * as Option from "effect/Option";
13
+ import * as Path from "effect/Path";
14
+ import * as Result from "effect/Result";
15
+ import * as Schema from "effect/Schema";
16
+ import { PackageURL } from "packageurl-js";
17
+ import { parseTomlStringEntries, readTomlSection } from "@agentxm/extension-workspace";
18
+ import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
19
+ import { decodeAxmMeta, decodePurl, parseJsonOptional, readFileOptional } from "./reader-io.js";
20
+ const mojoType = Schema.decodeUnknownSync(PackageTypeSchema)("mojo");
21
+ const condaType = Schema.decodeUnknownSync(PackageTypeSchema)("conda");
22
+ /**
23
+ * Known Mojo-specific packages that should produce pkg:mojo purls
24
+ * rather than pkg:conda purls.
25
+ */
26
+ const MOJO_SPECIFIC_PACKAGES = new Set([
27
+ "max",
28
+ "mojo",
29
+ "modular",
30
+ "mojo-stdlib",
31
+ "max-engine",
32
+ "max-graph",
33
+ "max-serving",
34
+ ]);
35
+ /**
36
+ * Returns true if the specifier is an exact version (no range operators).
37
+ */
38
+ const isExactVersion = (specifier) => /^\d+\.\d+\.\d+(?:[-+][a-zA-Z0-9._+-]*)?$/.test(specifier);
39
+ /**
40
+ * Parse a TOML [dependencies] section using simple regex.
41
+ * Returns an array of { name, version } entries.
42
+ */
43
+ const parseTomlDependencies = (content) => {
44
+ const depsBlock = readTomlSection(content, "dependencies");
45
+ if (depsBlock === undefined)
46
+ return [];
47
+ return parseTomlStringEntries(depsBlock).map(({ key, value }) => ({ name: key, version: value }));
48
+ };
49
+ /**
50
+ * Mojo package detector.
51
+ *
52
+ * Scans `pixi.toml` in the project directory. Mojo-specific packages produce `pkg:mojo`
53
+ * purls, while conda packages produce `pkg:conda` purls.
54
+ *
55
+ * @experimental This API is unstable and may change without notice.
56
+ */
57
+ export const mojoDetector = {
58
+ type: mojoType,
59
+ detect: Effect.fn("detect.mojo")(function* (projectDir) {
60
+ const path = yield* Path.Path;
61
+ const pixiPath = path.join(projectDir, "pixi.toml");
62
+ const content = yield* readFileOptional(pixiPath);
63
+ if (Option.isNone(content))
64
+ return [];
65
+ const deps = parseTomlDependencies(content.value);
66
+ if (deps.length === 0) {
67
+ // Check for malformed TOML if the file is non-trivial
68
+ if (content.value.includes("[dependencies]")) {
69
+ // Has section header but we couldn't parse any deps - might be valid (empty deps)
70
+ return [];
71
+ }
72
+ // File exists but no [dependencies] section
73
+ return [];
74
+ }
75
+ const results = [];
76
+ for (const dep of deps) {
77
+ const isMojoSpecific = MOJO_SPECIFIC_PACKAGES.has(dep.name);
78
+ const version = isExactVersion(dep.version) ? dep.version : undefined;
79
+ if (isMojoSpecific) {
80
+ const purl = new PackageURL("mojo", null, dep.name, version ?? null, null, null);
81
+ const purlParts = decodePurl(purl.toString());
82
+ results.push({ purl: purlParts, type: mojoType, source: pixiPath });
83
+ }
84
+ else {
85
+ const purl = new PackageURL("conda", null, dep.name, version ?? null, null, null);
86
+ const purlParts = decodePurl(purl.toString());
87
+ results.push({ purl: purlParts, type: condaType, source: pixiPath });
88
+ }
89
+ }
90
+ return results;
91
+ }, Effect.annotateLogs({ detector: "mojo" }), Effect.withSpan("detect.mojo")),
92
+ };
93
+ /**
94
+ * Mojo package reader.
95
+ *
96
+ * Reads `axm.json` sidecar files from `.pixi/envs/<env>/conda-meta/`
97
+ * in the project directory for each detected Mojo/conda package.
98
+ *
99
+ * @experimental This API is unstable and may change without notice.
100
+ */
101
+ export const mojoReader = {
102
+ type: mojoType,
103
+ read: Effect.fn("read.mojo")(function* (pkg) {
104
+ const path = yield* Path.Path;
105
+ const fs = yield* FileSystem.FileSystem;
106
+ // Derive project directory from the source manifest path
107
+ const projectDir = path.dirname(pkg.source);
108
+ const pixiEnvsDir = path.join(projectDir, ".pixi", "envs");
109
+ // Check if .pixi/envs exists
110
+ const envsDirExists = yield* fs.exists(pixiEnvsDir).pipe(Effect.option);
111
+ if (Option.isNone(envsDirExists) || !envsDirExists.value)
112
+ return Option.none();
113
+ // Scan environment directories
114
+ const envDirs = yield* fs.readDirectory(pixiEnvsDir).pipe(Effect.option);
115
+ if (Option.isNone(envDirs))
116
+ return Option.none();
117
+ for (const envDir of envDirs.value) {
118
+ const condaMetaDir = path.join(pixiEnvsDir, envDir, "conda-meta");
119
+ const condaMetaExists = yield* fs.exists(condaMetaDir).pipe(Effect.option);
120
+ if (Option.isNone(condaMetaExists) || !condaMetaExists.value)
121
+ continue;
122
+ // Scan for package directories matching the package name
123
+ const metaEntries = yield* fs.readDirectory(condaMetaDir).pipe(Effect.option);
124
+ if (Option.isNone(metaEntries))
125
+ continue;
126
+ for (const entry of metaEntries.value) {
127
+ // Match entries that start with the package name
128
+ if (!entry.startsWith(pkg.purl.name))
129
+ continue;
130
+ const axmJsonPath = path.join(condaMetaDir, entry, "axm.json");
131
+ const content = yield* readFileOptional(axmJsonPath);
132
+ if (Option.isNone(content))
133
+ continue;
134
+ const parsed = yield* parseJsonOptional(content.value, `${pkg.purl.name}/axm.json`);
135
+ if (Option.isNone(parsed))
136
+ continue;
137
+ const metaResult = decodeAxmMeta(parsed.value);
138
+ if (Result.isFailure(metaResult)) {
139
+ yield* Effect.logWarning(`Invalid axm metadata in pixi cache for ${pkg.purl.name}: schema validation failed`);
140
+ continue;
141
+ }
142
+ return Option.some(metaResult.success.extensions);
143
+ }
144
+ }
145
+ return Option.none();
146
+ }, Effect.annotateLogs({ reader: "mojo" }), Effect.withSpan("read.mojo")),
147
+ };
148
+ //# sourceMappingURL=mojo.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * npm package detector and reader for package-compatibility discovery.
3
+ *
4
+ * @experimental This API is unstable and may change without notice.
5
+ * @packageDocumentation
6
+ */
7
+ import type { PackageDetector, PackageReader } from "./types.js";
8
+ /**
9
+ * npm package detector.
10
+ *
11
+ * Scans `package.json` in the project directory and extracts dependencies
12
+ * from `dependencies`, `devDependencies`, and `peerDependencies`.
13
+ *
14
+ * @experimental This API is unstable and may change without notice.
15
+ */
16
+ export declare const npmDetector: PackageDetector;
17
+ /**
18
+ * npm package reader.
19
+ *
20
+ * Reads `node_modules/<name>/package.json` for each detected npm package
21
+ * and extracts the `"axm"` field containing recommendation metadata.
22
+ *
23
+ * @experimental This API is unstable and may change without notice.
24
+ */
25
+ export declare const npmReader: PackageReader;
26
+ //# sourceMappingURL=npm.d.ts.map