@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.
- package/LICENSE +110 -0
- package/dist/src/discover.d.ts +34 -0
- package/dist/src/discover.js +133 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +12 -0
- package/dist/src/internal/environment.d.ts +14 -0
- package/dist/src/internal/environment.js +15 -0
- package/dist/src/packaging/bazel.d.ts +27 -0
- package/dist/src/packaging/bazel.js +125 -0
- package/dist/src/packaging/cargo.d.ts +32 -0
- package/dist/src/packaging/cargo.js +270 -0
- package/dist/src/packaging/cocoapods.d.ts +26 -0
- package/dist/src/packaging/cocoapods.js +187 -0
- package/dist/src/packaging/composer.d.ts +26 -0
- package/dist/src/packaging/composer.js +144 -0
- package/dist/src/packaging/conan.d.ts +30 -0
- package/dist/src/packaging/conan.js +238 -0
- package/dist/src/packaging/conda.d.ts +26 -0
- package/dist/src/packaging/conda.js +320 -0
- package/dist/src/packaging/cpan.d.ts +29 -0
- package/dist/src/packaging/cpan.js +189 -0
- package/dist/src/packaging/cran.d.ts +29 -0
- package/dist/src/packaging/cran.js +176 -0
- package/dist/src/packaging/detect.d.ts +16 -0
- package/dist/src/packaging/detect.js +25 -0
- package/dist/src/packaging/detected-package.d.ts +14 -0
- package/dist/src/packaging/detected-package.js +20 -0
- package/dist/src/packaging/docker.d.ts +27 -0
- package/dist/src/packaging/docker.js +256 -0
- package/dist/src/packaging/gem.d.ts +26 -0
- package/dist/src/packaging/gem.js +234 -0
- package/dist/src/packaging/golang.d.ts +26 -0
- package/dist/src/packaging/golang.js +169 -0
- package/dist/src/packaging/hackage.d.ts +26 -0
- package/dist/src/packaging/hackage.js +305 -0
- package/dist/src/packaging/hex.d.ts +28 -0
- package/dist/src/packaging/hex.js +186 -0
- package/dist/src/packaging/huggingface.d.ts +20 -0
- package/dist/src/packaging/huggingface.js +112 -0
- package/dist/src/packaging/index.d.ts +44 -0
- package/dist/src/packaging/index.js +120 -0
- package/dist/src/packaging/jsr.d.ts +30 -0
- package/dist/src/packaging/jsr.js +227 -0
- package/dist/src/packaging/julia.d.ts +27 -0
- package/dist/src/packaging/julia.js +165 -0
- package/dist/src/packaging/luarocks.d.ts +28 -0
- package/dist/src/packaging/luarocks.js +159 -0
- package/dist/src/packaging/maven.d.ts +27 -0
- package/dist/src/packaging/maven.js +471 -0
- package/dist/src/packaging/mojo.d.ts +29 -0
- package/dist/src/packaging/mojo.js +148 -0
- package/dist/src/packaging/npm.d.ts +26 -0
- package/dist/src/packaging/npm.js +190 -0
- package/dist/src/packaging/nuget.d.ts +26 -0
- package/dist/src/packaging/nuget.js +240 -0
- package/dist/src/packaging/opam.d.ts +27 -0
- package/dist/src/packaging/opam.js +287 -0
- package/dist/src/packaging/pub.d.ts +26 -0
- package/dist/src/packaging/pub.js +357 -0
- package/dist/src/packaging/pypi.d.ts +23 -0
- package/dist/src/packaging/pypi.js +448 -0
- package/dist/src/packaging/read.d.ts +20 -0
- package/dist/src/packaging/read.js +31 -0
- package/dist/src/packaging/reader-io.d.ts +29 -0
- package/dist/src/packaging/reader-io.js +29 -0
- package/dist/src/packaging/swift.d.ts +26 -0
- package/dist/src/packaging/swift.js +141 -0
- package/dist/src/packaging/types.d.ts +43 -0
- package/dist/src/packaging/types.js +8 -0
- package/dist/src/packaging/zig.d.ts +32 -0
- package/dist/src/packaging/zig.js +151 -0
- package/package.json +54 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSR package detector and Deno reader for package-compatibility discovery.
|
|
3
|
+
*
|
|
4
|
+
* Parses `deno.json`/`deno.jsonc` for `jsr:@scope/name` imports and reads
|
|
5
|
+
* axm metadata from Deno's module cache.
|
|
6
|
+
*
|
|
7
|
+
* @experimental This API is unstable and may change without notice.
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
// Intentional escape hatch: node:os homedir() has no @effect/platform equivalent.
|
|
11
|
+
import * as os from "node:os";
|
|
12
|
+
import * as Effect from "effect/Effect";
|
|
13
|
+
import * as FileSystem from "effect/FileSystem";
|
|
14
|
+
import * as Option from "effect/Option";
|
|
15
|
+
import * as Path from "effect/Path";
|
|
16
|
+
import * as Result from "effect/Result";
|
|
17
|
+
import * as Schema from "effect/Schema";
|
|
18
|
+
import { PackageURL } from "packageurl-js";
|
|
19
|
+
import { readEnv } from "../internal/environment.js";
|
|
20
|
+
import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
|
|
21
|
+
import { decodeAxmMeta, decodePurl, parseJsonOptional, readFileOptional } from "./reader-io.js";
|
|
22
|
+
const jsrType = Schema.decodeUnknownSync(PackageTypeSchema)("jsr");
|
|
23
|
+
/**
|
|
24
|
+
* Strip single-line (//) and multi-line comments from JSONC content.
|
|
25
|
+
* Simple approach that handles most common cases.
|
|
26
|
+
*/
|
|
27
|
+
const stripJsoncComments = (content) => {
|
|
28
|
+
let result = "";
|
|
29
|
+
let i = 0;
|
|
30
|
+
let inString = false;
|
|
31
|
+
while (i < content.length) {
|
|
32
|
+
// Track string boundaries
|
|
33
|
+
if (content[i] === '"' && (i === 0 || content[i - 1] !== "\\")) {
|
|
34
|
+
inString = !inString;
|
|
35
|
+
result += content[i];
|
|
36
|
+
i++;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (inString) {
|
|
40
|
+
result += content[i];
|
|
41
|
+
i++;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
// Single-line comment
|
|
45
|
+
if (content[i] === "/" && content[i + 1] === "/") {
|
|
46
|
+
while (i < content.length && content[i] !== "\n")
|
|
47
|
+
i++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
// Multi-line comment
|
|
51
|
+
if (content[i] === "/" && content[i + 1] === "*") {
|
|
52
|
+
i += 2;
|
|
53
|
+
while (i < content.length - 1 && !(content[i] === "*" && content[i + 1] === "/"))
|
|
54
|
+
i++;
|
|
55
|
+
i += 2; // skip */
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
result += content[i];
|
|
59
|
+
i++;
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Returns true if the specifier is an exact semver version (no range operators).
|
|
65
|
+
*/
|
|
66
|
+
const isExactVersion = (version) => /^\d+\.\d+\.\d+(?:[-+][a-zA-Z0-9._+-]*)?$/.test(version);
|
|
67
|
+
/**
|
|
68
|
+
* Parse a JSR import specifier like `jsr:@scope/name@version`.
|
|
69
|
+
* Returns parsed parts or undefined if not a JSR import.
|
|
70
|
+
*/
|
|
71
|
+
const parseJsrSpecifier = (specifier) => {
|
|
72
|
+
if (!specifier.startsWith("jsr:"))
|
|
73
|
+
return undefined;
|
|
74
|
+
const rest = specifier.slice(4); // Remove "jsr:" prefix
|
|
75
|
+
// Match @scope/name or @scope/name@version
|
|
76
|
+
const match = /^(@[^/]+)\/([^@]+)(?:@(.+))?$/.exec(rest);
|
|
77
|
+
if (match === null || match[1] === undefined || match[2] === undefined)
|
|
78
|
+
return undefined;
|
|
79
|
+
return {
|
|
80
|
+
scope: match[1],
|
|
81
|
+
name: match[2],
|
|
82
|
+
...(match[3] !== undefined ? { version: match[3] } : {}),
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Extract JSR imports from a parsed deno.json imports map.
|
|
87
|
+
*/
|
|
88
|
+
const extractJsrImports = (imports, source) => {
|
|
89
|
+
const results = [];
|
|
90
|
+
for (const specifier of Object.values(imports)) {
|
|
91
|
+
const parsed = parseJsrSpecifier(specifier);
|
|
92
|
+
if (parsed === undefined)
|
|
93
|
+
continue; // Skip non-JSR imports (npm:, etc.)
|
|
94
|
+
const version = parsed.version !== undefined && isExactVersion(parsed.version) ? parsed.version : undefined;
|
|
95
|
+
const purl = new PackageURL("jsr", parsed.scope, parsed.name, version ?? null, null, null);
|
|
96
|
+
const purlParts = decodePurl(purl.toString());
|
|
97
|
+
results.push({ purl: purlParts, type: jsrType, source });
|
|
98
|
+
}
|
|
99
|
+
return results;
|
|
100
|
+
};
|
|
101
|
+
/** Schema to extract the optional "imports" field from deno.json. */
|
|
102
|
+
const DenoImportsSchema = Schema.Struct({
|
|
103
|
+
imports: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
|
104
|
+
});
|
|
105
|
+
const decodeDenoImports = Schema.decodeUnknownResult(DenoImportsSchema);
|
|
106
|
+
/**
|
|
107
|
+
* JSR package detector.
|
|
108
|
+
*
|
|
109
|
+
* Scans `deno.json` and `deno.jsonc` in the project directory and extracts
|
|
110
|
+
* `jsr:@scope/name` imports, producing `pkg:jsr/<scope>/<name>` purls.
|
|
111
|
+
* npm-prefixed imports are skipped.
|
|
112
|
+
*
|
|
113
|
+
* @experimental This API is unstable and may change without notice.
|
|
114
|
+
*/
|
|
115
|
+
export const jsrDetector = {
|
|
116
|
+
type: jsrType,
|
|
117
|
+
detect: Effect.fn("detect.jsr")(function* (projectDir) {
|
|
118
|
+
const path = yield* Path.Path;
|
|
119
|
+
// Try deno.json first, then deno.jsonc
|
|
120
|
+
const denoJsonPath = path.join(projectDir, "deno.json");
|
|
121
|
+
const denoJsoncPath = path.join(projectDir, "deno.jsonc");
|
|
122
|
+
let content = yield* readFileOptional(denoJsonPath);
|
|
123
|
+
let source = denoJsonPath;
|
|
124
|
+
let isJsonc = false;
|
|
125
|
+
if (Option.isNone(content)) {
|
|
126
|
+
content = yield* readFileOptional(denoJsoncPath);
|
|
127
|
+
source = denoJsoncPath;
|
|
128
|
+
isJsonc = true;
|
|
129
|
+
}
|
|
130
|
+
if (Option.isNone(content))
|
|
131
|
+
return [];
|
|
132
|
+
// Strip comments for .jsonc files
|
|
133
|
+
const jsonContent = isJsonc ? stripJsoncComments(content.value) : content.value;
|
|
134
|
+
const parsed = yield* parseJsonOptional(jsonContent, source);
|
|
135
|
+
if (Option.isNone(parsed))
|
|
136
|
+
return [];
|
|
137
|
+
const importsResult = decodeDenoImports(parsed.value);
|
|
138
|
+
if (Result.isFailure(importsResult))
|
|
139
|
+
return [];
|
|
140
|
+
const imports = importsResult.success.imports;
|
|
141
|
+
if (imports === undefined)
|
|
142
|
+
return [];
|
|
143
|
+
return extractJsrImports(imports, source);
|
|
144
|
+
}, Effect.annotateLogs({ detector: "jsr" }), Effect.withSpan("detect.jsr")),
|
|
145
|
+
};
|
|
146
|
+
/** Schema to extract the optional "axm" field from deno.json. */
|
|
147
|
+
const AxmContainerSchema = Schema.Struct({
|
|
148
|
+
axm: Schema.optional(Schema.Unknown),
|
|
149
|
+
});
|
|
150
|
+
const decodeAxmContainer = Schema.decodeUnknownResult(AxmContainerSchema);
|
|
151
|
+
/**
|
|
152
|
+
* Resolve the Deno cache directory.
|
|
153
|
+
* Uses $DENO_DIR if set, otherwise platform-specific defaults.
|
|
154
|
+
*/
|
|
155
|
+
const resolveDenoDir = () => Effect.sync(() => {
|
|
156
|
+
const denoDir = readEnv("DENO_DIR");
|
|
157
|
+
if (denoDir !== undefined && denoDir !== "")
|
|
158
|
+
return denoDir;
|
|
159
|
+
// Platform-specific defaults
|
|
160
|
+
if (process.platform === "darwin") {
|
|
161
|
+
return `${os.homedir()}/Library/Caches/deno`;
|
|
162
|
+
}
|
|
163
|
+
return `${os.homedir()}/.cache/deno`;
|
|
164
|
+
});
|
|
165
|
+
/**
|
|
166
|
+
* Deno package reader.
|
|
167
|
+
*
|
|
168
|
+
* Reads axm metadata from cached module metadata in Deno's module cache.
|
|
169
|
+
* Checks for an `"axm"` field in cached `deno.json` files.
|
|
170
|
+
*
|
|
171
|
+
* @experimental This API is unstable and may change without notice.
|
|
172
|
+
*/
|
|
173
|
+
export const denoReader = {
|
|
174
|
+
type: jsrType,
|
|
175
|
+
read: Effect.fn("read.deno")(function* (pkg) {
|
|
176
|
+
const path = yield* Path.Path;
|
|
177
|
+
const fs = yield* FileSystem.FileSystem;
|
|
178
|
+
const denoDir = yield* resolveDenoDir();
|
|
179
|
+
// Check if deno cache directory exists
|
|
180
|
+
const denoDirExists = yield* fs.exists(denoDir).pipe(Effect.option);
|
|
181
|
+
if (Option.isNone(denoDirExists) || !denoDirExists.value)
|
|
182
|
+
return Option.none();
|
|
183
|
+
const pkgNamespace = pkg.purl.namespace;
|
|
184
|
+
if (pkgNamespace === undefined)
|
|
185
|
+
return Option.none();
|
|
186
|
+
const scope = pkgNamespace;
|
|
187
|
+
// Look for cached module metadata
|
|
188
|
+
// Deno caches JSR packages in registry cache
|
|
189
|
+
const registryCacheDir = path.join(denoDir, "registries", "jsr.io");
|
|
190
|
+
const cacheDirExists = yield* fs.exists(registryCacheDir).pipe(Effect.option);
|
|
191
|
+
if (Option.isNone(cacheDirExists) || !cacheDirExists.value)
|
|
192
|
+
return Option.none();
|
|
193
|
+
// Check the cached package directory for deno.json with axm field
|
|
194
|
+
const pkgCacheDir = path.join(registryCacheDir, scope, pkg.purl.name);
|
|
195
|
+
const pkgDirExists = yield* fs.exists(pkgCacheDir).pipe(Effect.option);
|
|
196
|
+
if (Option.isNone(pkgDirExists) || !pkgDirExists.value)
|
|
197
|
+
return Option.none();
|
|
198
|
+
// Scan version directories for deno.json with axm field
|
|
199
|
+
const versionDirs = yield* fs.readDirectory(pkgCacheDir).pipe(Effect.option);
|
|
200
|
+
if (Option.isNone(versionDirs))
|
|
201
|
+
return Option.none();
|
|
202
|
+
for (const versionDir of versionDirs.value) {
|
|
203
|
+
const denoJsonPath = path.join(pkgCacheDir, versionDir, "deno.json");
|
|
204
|
+
const content = yield* readFileOptional(denoJsonPath);
|
|
205
|
+
if (Option.isNone(content))
|
|
206
|
+
continue;
|
|
207
|
+
const parsed = yield* parseJsonOptional(content.value, `${scope}/${pkg.purl.name}/deno.json`);
|
|
208
|
+
if (Option.isNone(parsed))
|
|
209
|
+
continue;
|
|
210
|
+
// Extract and validate the "axm" field
|
|
211
|
+
const axmContainerResult = decodeAxmContainer(parsed.value);
|
|
212
|
+
if (Result.isFailure(axmContainerResult))
|
|
213
|
+
continue;
|
|
214
|
+
const axmRaw = axmContainerResult.success.axm;
|
|
215
|
+
if (axmRaw === undefined)
|
|
216
|
+
continue;
|
|
217
|
+
const metaResult = decodeAxmMeta(axmRaw);
|
|
218
|
+
if (Result.isFailure(metaResult)) {
|
|
219
|
+
yield* Effect.logWarning(`Invalid axm metadata in deno cache for ${scope}/${pkg.purl.name}: schema validation failed`);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
return Option.some(metaResult.success.extensions);
|
|
223
|
+
}
|
|
224
|
+
return Option.none();
|
|
225
|
+
}, Effect.annotateLogs({ reader: "deno" }), Effect.withSpan("read.deno")),
|
|
226
|
+
};
|
|
227
|
+
//# sourceMappingURL=jsr.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Julia 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
|
+
* Julia package detector.
|
|
10
|
+
*
|
|
11
|
+
* Scans `Project.toml` in the project directory and extracts dependencies
|
|
12
|
+
* from the `[deps]` section. All purls are versionless since Julia
|
|
13
|
+
* identifies packages by UUID.
|
|
14
|
+
*
|
|
15
|
+
* @experimental This API is unstable and may change without notice.
|
|
16
|
+
*/
|
|
17
|
+
export declare const juliaDetector: PackageDetector;
|
|
18
|
+
/**
|
|
19
|
+
* Julia package reader.
|
|
20
|
+
*
|
|
21
|
+
* Reads `[axm]` section from `~/.julia/packages/<pkg>/<hash>/Project.toml`
|
|
22
|
+
* for each detected Julia package.
|
|
23
|
+
*
|
|
24
|
+
* @experimental This API is unstable and may change without notice.
|
|
25
|
+
*/
|
|
26
|
+
export declare const juliaReader: PackageReader;
|
|
27
|
+
//# sourceMappingURL=julia.d.ts.map
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Julia 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 { PackageURL } from "packageurl-js";
|
|
16
|
+
import { parseTomlValue } from "@agentxm/extension-workspace";
|
|
17
|
+
import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
|
|
18
|
+
import { decodeAxmMeta, decodePurl, readFileOptional } from "./reader-io.js";
|
|
19
|
+
const juliaType = Schema.decodeUnknownSync(PackageTypeSchema)("julia");
|
|
20
|
+
/** UUID pattern for Julia dependency values. */
|
|
21
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
22
|
+
/**
|
|
23
|
+
* Parse a simple TOML [deps] section from Project.toml content.
|
|
24
|
+
* Julia's Project.toml uses TOML format with [deps] section containing:
|
|
25
|
+
* PackageName = "uuid-string"
|
|
26
|
+
*/
|
|
27
|
+
const parseDepsSection = (content, source) => {
|
|
28
|
+
const results = [];
|
|
29
|
+
const lines = content.split("\n");
|
|
30
|
+
let inDepsSection = false;
|
|
31
|
+
for (const line of lines) {
|
|
32
|
+
const trimmed = line.trim();
|
|
33
|
+
// Section headers
|
|
34
|
+
if (trimmed.startsWith("[")) {
|
|
35
|
+
inDepsSection = trimmed === "[deps]";
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (!inDepsSection)
|
|
39
|
+
continue;
|
|
40
|
+
// Skip empty lines and comments
|
|
41
|
+
if (trimmed === "" || trimmed.startsWith("#"))
|
|
42
|
+
continue;
|
|
43
|
+
// Parse key = "value" entries
|
|
44
|
+
const match = /^(\S+)\s*=\s*"([^"]*)"/.exec(trimmed);
|
|
45
|
+
if (match === null)
|
|
46
|
+
continue;
|
|
47
|
+
const name = match[1];
|
|
48
|
+
const value = match[2];
|
|
49
|
+
if (name === undefined || value === undefined)
|
|
50
|
+
continue;
|
|
51
|
+
// Validate it looks like a UUID
|
|
52
|
+
if (!UUID_PATTERN.test(value))
|
|
53
|
+
continue;
|
|
54
|
+
// Julia deps are always versionless (identified by UUID)
|
|
55
|
+
const purl = new PackageURL("julia", null, name, null, null, null);
|
|
56
|
+
const purlParts = decodePurl(purl.toString());
|
|
57
|
+
results.push({ purl: purlParts, type: juliaType, source });
|
|
58
|
+
}
|
|
59
|
+
return results;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Julia package detector.
|
|
63
|
+
*
|
|
64
|
+
* Scans `Project.toml` in the project directory and extracts dependencies
|
|
65
|
+
* from the `[deps]` section. All purls are versionless since Julia
|
|
66
|
+
* identifies packages by UUID.
|
|
67
|
+
*
|
|
68
|
+
* @experimental This API is unstable and may change without notice.
|
|
69
|
+
*/
|
|
70
|
+
export const juliaDetector = {
|
|
71
|
+
type: juliaType,
|
|
72
|
+
detect: Effect.fn("detect.julia")(function* (projectDir) {
|
|
73
|
+
const path = yield* Path.Path;
|
|
74
|
+
const projectTomlPath = path.join(projectDir, "Project.toml");
|
|
75
|
+
const content = yield* readFileOptional(projectTomlPath);
|
|
76
|
+
if (Option.isNone(content))
|
|
77
|
+
return [];
|
|
78
|
+
const deps = parseDepsSection(content.value, projectTomlPath);
|
|
79
|
+
if (deps.length === 0 && content.value.trim().length > 0) {
|
|
80
|
+
// Content exists but no deps found - check if it looks like valid TOML
|
|
81
|
+
if (!content.value.includes("=") && !content.value.includes("[")) {
|
|
82
|
+
yield* Effect.logWarning("Malformed Project.toml, skipping");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return deps;
|
|
86
|
+
}, Effect.annotateLogs({ detector: "julia" }), Effect.withSpan("detect.julia")),
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Parse a simple TOML [axm] section from Project.toml content.
|
|
90
|
+
* Returns the parsed fields as a record, or undefined if no [axm] section found.
|
|
91
|
+
*/
|
|
92
|
+
const parseAxmSection = (content) => {
|
|
93
|
+
const lines = content.split("\n");
|
|
94
|
+
let inAxmSection = false;
|
|
95
|
+
const fields = {};
|
|
96
|
+
let foundSection = false;
|
|
97
|
+
for (const line of lines) {
|
|
98
|
+
const trimmed = line.trim();
|
|
99
|
+
// Section headers
|
|
100
|
+
if (trimmed.startsWith("[")) {
|
|
101
|
+
if (inAxmSection)
|
|
102
|
+
break; // End of [axm] section
|
|
103
|
+
inAxmSection = trimmed === "[axm]";
|
|
104
|
+
if (inAxmSection)
|
|
105
|
+
foundSection = true;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (!inAxmSection)
|
|
109
|
+
continue;
|
|
110
|
+
// Skip empty lines and comments
|
|
111
|
+
if (trimmed === "" || trimmed.startsWith("#"))
|
|
112
|
+
continue;
|
|
113
|
+
// Parse key = value entries
|
|
114
|
+
const match = /^(\S+)\s*=\s*(.+)$/.exec(trimmed);
|
|
115
|
+
if (match === null)
|
|
116
|
+
continue;
|
|
117
|
+
const key = match[1];
|
|
118
|
+
const rawValue = match[2]?.trim();
|
|
119
|
+
if (key === undefined || rawValue === undefined)
|
|
120
|
+
continue;
|
|
121
|
+
// Parse TOML values
|
|
122
|
+
fields[key] = parseTomlValue(rawValue);
|
|
123
|
+
}
|
|
124
|
+
return foundSection ? fields : undefined;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Julia package reader.
|
|
128
|
+
*
|
|
129
|
+
* Reads `[axm]` section from `~/.julia/packages/<pkg>/<hash>/Project.toml`
|
|
130
|
+
* for each detected Julia package.
|
|
131
|
+
*
|
|
132
|
+
* @experimental This API is unstable and may change without notice.
|
|
133
|
+
*/
|
|
134
|
+
export const juliaReader = {
|
|
135
|
+
type: juliaType,
|
|
136
|
+
read: Effect.fn("read.julia")(function* (pkg) {
|
|
137
|
+
const path = yield* Path.Path;
|
|
138
|
+
const fs = yield* FileSystem.FileSystem;
|
|
139
|
+
const pkgName = pkg.purl.name;
|
|
140
|
+
const home = os.homedir();
|
|
141
|
+
const juliaPkgsDir = path.join(home, ".julia", "packages", pkgName);
|
|
142
|
+
// Scan hash directories
|
|
143
|
+
const hashDirs = yield* fs.readDirectory(juliaPkgsDir).pipe(Effect.option);
|
|
144
|
+
if (Option.isNone(hashDirs))
|
|
145
|
+
return Option.none();
|
|
146
|
+
// Check each hash directory for Project.toml with [axm] section
|
|
147
|
+
for (const hashDir of hashDirs.value) {
|
|
148
|
+
const projectTomlPath = path.join(juliaPkgsDir, hashDir, "Project.toml");
|
|
149
|
+
const content = yield* readFileOptional(projectTomlPath);
|
|
150
|
+
if (Option.isNone(content))
|
|
151
|
+
continue;
|
|
152
|
+
const axmFields = parseAxmSection(content.value);
|
|
153
|
+
if (axmFields === undefined)
|
|
154
|
+
continue;
|
|
155
|
+
const metaResult = decodeAxmMeta(axmFields);
|
|
156
|
+
if (Result.isFailure(metaResult)) {
|
|
157
|
+
yield* Effect.logWarning(`Invalid axm metadata in ${pkgName}: schema validation failed`);
|
|
158
|
+
return Option.none();
|
|
159
|
+
}
|
|
160
|
+
return Option.some(metaResult.success.extensions);
|
|
161
|
+
}
|
|
162
|
+
return Option.none();
|
|
163
|
+
}, Effect.annotateLogs({ reader: "julia" }), Effect.withSpan("read.julia")),
|
|
164
|
+
};
|
|
165
|
+
//# sourceMappingURL=julia.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LuaRocks 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
|
+
* LuaRocks package detector.
|
|
10
|
+
*
|
|
11
|
+
* Scans `*.rockspec` files in the project directory and extracts
|
|
12
|
+
* dependencies from the `dependencies` table. Skips the `lua` runtime.
|
|
13
|
+
* Multiple rockspec files are processed and results deduplicated.
|
|
14
|
+
*
|
|
15
|
+
* @experimental This API is unstable and may change without notice.
|
|
16
|
+
*/
|
|
17
|
+
export declare const luarocksDetector: PackageDetector;
|
|
18
|
+
/**
|
|
19
|
+
* LuaRocks package reader.
|
|
20
|
+
*
|
|
21
|
+
* Reads `axm/axm.json` sidecar from the LuaRocks install tree.
|
|
22
|
+
* Checks system tree (`/usr/local/lib/luarocks/rocks-5.x/`) and
|
|
23
|
+
* user tree (`~/.luarocks/lib/luarocks/rocks-5.x/`).
|
|
24
|
+
*
|
|
25
|
+
* @experimental This API is unstable and may change without notice.
|
|
26
|
+
*/
|
|
27
|
+
export declare const luarocksReader: PackageReader;
|
|
28
|
+
//# sourceMappingURL=luarocks.d.ts.map
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LuaRocks 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 { PackageURL } from "packageurl-js";
|
|
16
|
+
import { PackageTypeSchema } from "@agentxm/extension-model/unstable/packaging/package-type";
|
|
17
|
+
import { decodeAxmMeta, decodePurl, parseJsonOptional, readFileOptional } from "./reader-io.js";
|
|
18
|
+
const luarocksType = Schema.decodeUnknownSync(PackageTypeSchema)("luarocks");
|
|
19
|
+
/** Dependencies to skip (Lua runtime itself). */
|
|
20
|
+
const SKIP_DEPS = new Set(["lua"]);
|
|
21
|
+
/**
|
|
22
|
+
* Parse a single dependency string from a rockspec dependencies table.
|
|
23
|
+
* Format: "name [operator version]"
|
|
24
|
+
* Examples: "luasocket >= 3.0", "luafilesystem", "cjson == 1.0.0-1"
|
|
25
|
+
*/
|
|
26
|
+
const parseLuaDep = (entry) => {
|
|
27
|
+
const trimmed = entry.trim().replace(/^["']|["']$/g, "");
|
|
28
|
+
if (trimmed === "")
|
|
29
|
+
return undefined;
|
|
30
|
+
const parts = trimmed.split(/\s+/);
|
|
31
|
+
const name = parts[0];
|
|
32
|
+
if (name === undefined || name === "")
|
|
33
|
+
return undefined;
|
|
34
|
+
if (SKIP_DEPS.has(name.toLowerCase()))
|
|
35
|
+
return undefined;
|
|
36
|
+
// Check for exact version: name == version
|
|
37
|
+
if (parts[1] === "==" && parts[2] !== undefined) {
|
|
38
|
+
return { name, version: parts[2] };
|
|
39
|
+
}
|
|
40
|
+
// Any other operator or no version => versionless
|
|
41
|
+
return { name };
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Parse a rockspec dependencies table from file content.
|
|
45
|
+
* The dependencies table has the form:
|
|
46
|
+
* dependencies = { "dep1", "dep2 >= 1.0", ... }
|
|
47
|
+
* This is a Lua table literal, so we use regex to extract entries.
|
|
48
|
+
*/
|
|
49
|
+
const parseRockspecDeps = (content, source) => {
|
|
50
|
+
const results = [];
|
|
51
|
+
// Find dependencies table - may span multiple lines
|
|
52
|
+
const depsRegex = /dependencies\s*=\s*\{([^}]*)}/s;
|
|
53
|
+
const match = depsRegex.exec(content);
|
|
54
|
+
if (match === null || match[1] === undefined)
|
|
55
|
+
return [];
|
|
56
|
+
// Extract quoted strings from the table
|
|
57
|
+
const stringRegex = /["']([^"']+)["']/g;
|
|
58
|
+
let strMatch;
|
|
59
|
+
while ((strMatch = stringRegex.exec(match[1])) !== null) {
|
|
60
|
+
const entry = strMatch[1];
|
|
61
|
+
if (entry === undefined)
|
|
62
|
+
continue;
|
|
63
|
+
const parsed = parseLuaDep(entry);
|
|
64
|
+
if (parsed === undefined)
|
|
65
|
+
continue;
|
|
66
|
+
const purl = new PackageURL("luarocks", null, parsed.name, parsed.version ?? null, null, null);
|
|
67
|
+
const purlParts = decodePurl(purl.toString());
|
|
68
|
+
results.push({ purl: purlParts, type: luarocksType, source });
|
|
69
|
+
}
|
|
70
|
+
return results;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* LuaRocks package detector.
|
|
74
|
+
*
|
|
75
|
+
* Scans `*.rockspec` files in the project directory and extracts
|
|
76
|
+
* dependencies from the `dependencies` table. Skips the `lua` runtime.
|
|
77
|
+
* Multiple rockspec files are processed and results deduplicated.
|
|
78
|
+
*
|
|
79
|
+
* @experimental This API is unstable and may change without notice.
|
|
80
|
+
*/
|
|
81
|
+
export const luarocksDetector = {
|
|
82
|
+
type: luarocksType,
|
|
83
|
+
detect: Effect.fn("detect.luarocks")(function* (projectDir) {
|
|
84
|
+
const path = yield* Path.Path;
|
|
85
|
+
const fs = yield* FileSystem.FileSystem;
|
|
86
|
+
const entries = yield* fs.readDirectory(projectDir).pipe(Effect.option);
|
|
87
|
+
if (Option.isNone(entries))
|
|
88
|
+
return [];
|
|
89
|
+
const rockspecFiles = entries.value.filter((e) => e.endsWith(".rockspec"));
|
|
90
|
+
if (rockspecFiles.length === 0)
|
|
91
|
+
return [];
|
|
92
|
+
const allDeps = [];
|
|
93
|
+
const seenNames = new Set();
|
|
94
|
+
for (const rockspecFile of rockspecFiles) {
|
|
95
|
+
const filePath = path.join(projectDir, rockspecFile);
|
|
96
|
+
const content = yield* readFileOptional(filePath);
|
|
97
|
+
if (Option.isNone(content))
|
|
98
|
+
continue;
|
|
99
|
+
const deps = parseRockspecDeps(content.value, filePath);
|
|
100
|
+
if (deps.length === 0 && content.value.trim().length > 0) {
|
|
101
|
+
// Content exists but no deps - check if it looks like a valid rockspec
|
|
102
|
+
if (!content.value.includes("package") && !content.value.includes("rockspec_format")) {
|
|
103
|
+
yield* Effect.logWarning(`Malformed rockspec file: ${rockspecFile}, skipping`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const dep of deps) {
|
|
107
|
+
if (!seenNames.has(dep.purl.name)) {
|
|
108
|
+
seenNames.add(dep.purl.name);
|
|
109
|
+
allDeps.push(dep);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return allDeps;
|
|
114
|
+
}, Effect.annotateLogs({ detector: "luarocks" }), Effect.withSpan("detect.luarocks")),
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* LuaRocks package reader.
|
|
118
|
+
*
|
|
119
|
+
* Reads `axm/axm.json` sidecar from the LuaRocks install tree.
|
|
120
|
+
* Checks system tree (`/usr/local/lib/luarocks/rocks-5.x/`) and
|
|
121
|
+
* user tree (`~/.luarocks/lib/luarocks/rocks-5.x/`).
|
|
122
|
+
*
|
|
123
|
+
* @experimental This API is unstable and may change without notice.
|
|
124
|
+
*/
|
|
125
|
+
export const luarocksReader = {
|
|
126
|
+
type: luarocksType,
|
|
127
|
+
read: Effect.fn("read.luarocks")(function* (pkg) {
|
|
128
|
+
const path = yield* Path.Path;
|
|
129
|
+
const pkgName = pkg.purl.name;
|
|
130
|
+
const version = pkg.purl.version ?? "0.0.0-0";
|
|
131
|
+
const home = os.homedir();
|
|
132
|
+
// Lua version variants to check
|
|
133
|
+
const luaVersions = ["5.4", "5.3", "5.2", "5.1"];
|
|
134
|
+
// Candidate base paths
|
|
135
|
+
const basePaths = [
|
|
136
|
+
"/usr/local/lib/luarocks",
|
|
137
|
+
path.join(home, ".luarocks", "lib", "luarocks"),
|
|
138
|
+
];
|
|
139
|
+
for (const basePath of basePaths) {
|
|
140
|
+
for (const luaVer of luaVersions) {
|
|
141
|
+
const axmJsonPath = path.join(basePath, `rocks-${luaVer}`, pkgName, version, "axm", "axm.json");
|
|
142
|
+
const content = yield* readFileOptional(axmJsonPath);
|
|
143
|
+
if (Option.isNone(content))
|
|
144
|
+
continue;
|
|
145
|
+
const parsed = yield* parseJsonOptional(content.value, `${pkgName}/${version}/axm/axm.json`);
|
|
146
|
+
if (Option.isNone(parsed))
|
|
147
|
+
return Option.none();
|
|
148
|
+
const metaResult = decodeAxmMeta(parsed.value);
|
|
149
|
+
if (Result.isFailure(metaResult)) {
|
|
150
|
+
yield* Effect.logWarning(`Invalid axm metadata in ${pkgName}: schema validation failed`);
|
|
151
|
+
return Option.none();
|
|
152
|
+
}
|
|
153
|
+
return Option.some(metaResult.success.extensions);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return Option.none();
|
|
157
|
+
}, Effect.annotateLogs({ reader: "luarocks" }), Effect.withSpan("read.luarocks")),
|
|
158
|
+
};
|
|
159
|
+
//# sourceMappingURL=luarocks.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
import type { PackageDetector, PackageReader } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Maven package detector.
|
|
10
|
+
*
|
|
11
|
+
* Scans `pom.xml`, `build.gradle`, `build.gradle.kts`, and
|
|
12
|
+
* `gradle/libs.versions.toml` in the project directory. Also detects Clojure
|
|
13
|
+
* `deps.edn` Maven coordinates declared with `:mvn/version`.
|
|
14
|
+
*
|
|
15
|
+
* @experimental This API is unstable and may change without notice.
|
|
16
|
+
*/
|
|
17
|
+
export declare const mavenDetector: PackageDetector;
|
|
18
|
+
/**
|
|
19
|
+
* Maven package reader.
|
|
20
|
+
*
|
|
21
|
+
* Reads `META-INF/axm.json` from local JAR files in `~/.m2/repository/`
|
|
22
|
+
* or Gradle cache `~/.gradle/caches/modules-2/files-2.1/`.
|
|
23
|
+
*
|
|
24
|
+
* @experimental This API is unstable and may change without notice.
|
|
25
|
+
*/
|
|
26
|
+
export declare const mavenReader: PackageReader;
|
|
27
|
+
//# sourceMappingURL=maven.d.ts.map
|