@mhmdreza-rafiei/agentry 0.2.2 → 0.3.1

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.
@@ -0,0 +1,111 @@
1
+ import { frontmatterName, resolveSource } from "./discovery.mjs";
2
+ import https from "node:https";
3
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import http from "node:http";
7
+ function fetchUrl(url, maxRedirects = 5) {
8
+ return new Promise((resolve, reject) => {
9
+ const req = (url.startsWith("https://") ? https : http).get(url, { headers: {
10
+ "User-Agent": "agentry/0.3.1 (+https://github.com/mhmdreza-rafiei/agentry)",
11
+ Accept: "text/html,application/xhtml+xml,text/plain,text/markdown,application/json,*/*"
12
+ } }, (res) => {
13
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
14
+ if (maxRedirects <= 0) return reject(/* @__PURE__ */ new Error(`Too many redirects while fetching ${url}`));
15
+ return resolve(fetchUrl(new URL(res.headers.location, url).toString(), maxRedirects - 1));
16
+ }
17
+ if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) return reject(/* @__PURE__ */ new Error(`HTTP ${res.statusCode} when fetching ${url}`));
18
+ let body = "";
19
+ res.on("data", (chunk) => {
20
+ body += chunk;
21
+ });
22
+ res.on("end", () => {
23
+ resolve({
24
+ body,
25
+ status: res.statusCode || 200,
26
+ contentType: res.headers["content-type"] || "",
27
+ finalUrl: url
28
+ });
29
+ });
30
+ });
31
+ req.on("error", reject);
32
+ req.setTimeout(2e4, () => {
33
+ req.destroy();
34
+ reject(/* @__PURE__ */ new Error(`Request timed out while fetching ${url}`));
35
+ });
36
+ });
37
+ }
38
+ function cleanHtml(raw) {
39
+ return raw.replace(/\\/g, "");
40
+ }
41
+ async function resolveWebSource(url, opts = {}, fetcher = fetchUrl, sourceResolver = resolveSource) {
42
+ const gistMatch = url.match(/^https?:\/\/gist\.github\.com\/([a-zA-Z0-9_.-]+)\/([a-f0-9]+)/i);
43
+ if (gistMatch) return sourceResolver({
44
+ kind: "git-url",
45
+ raw: url,
46
+ url: `https://gist.github.com/${gistMatch[2]}.git`,
47
+ subpath: "",
48
+ local: null
49
+ }, opts);
50
+ const { body, contentType } = await fetcher(url);
51
+ const clean = cleanHtml(body);
52
+ const treeMatch = clean.match(/https:\/\/github\.com\/([\w.-]+\/[\w.-]+)\/tree\/([^/\s"'>]+)\/([^\s"'>]+)/i);
53
+ if (treeMatch) {
54
+ const ownerRepo = treeMatch[1];
55
+ const ref = treeMatch[2];
56
+ const subpath = treeMatch[3].replace(/\/+$/, "");
57
+ return sourceResolver({
58
+ kind: "github-url",
59
+ raw: url,
60
+ url: `https://github.com/${ownerRepo}.git`,
61
+ ref,
62
+ subpath,
63
+ local: null
64
+ }, opts);
65
+ }
66
+ const repoMatches = clean.match(/https:\/\/github\.com\/([\w.-]+\/[\w.-]+)(?:\.git)?\b/gi);
67
+ if (repoMatches) {
68
+ const ignoredPaths = /* @__PURE__ */ new Set([
69
+ "features",
70
+ "pricing",
71
+ "about",
72
+ "topics",
73
+ "trending",
74
+ "collections",
75
+ "site",
76
+ "security"
77
+ ]);
78
+ for (const match of repoMatches) {
79
+ const m = match.match(/https:\/\/github\.com\/([\w.-]+\/[\w.-]+)/i);
80
+ if (m) {
81
+ const ownerRepo = m[1];
82
+ const [owner, repo] = ownerRepo.split("/");
83
+ if (owner && repo && !ignoredPaths.has(owner.toLowerCase()) && !ignoredPaths.has(repo.toLowerCase())) return sourceResolver(`https://github.com/${ownerRepo}`, opts);
84
+ }
85
+ }
86
+ }
87
+ const isHtml = contentType.includes("html");
88
+ let content = body;
89
+ if (isHtml) {
90
+ const codeBlockMatch = body.match(/<pre[^>]*><code[^>]*>([\s\S]*?)<\/code><\/pre>/i);
91
+ if (codeBlockMatch && codeBlockMatch[1]) content = codeBlockMatch[1].replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
92
+ }
93
+ if (/^---\r?\n[\s\S]*?name:[\s\S]*?\r?\n---/m.test(content) || content.includes("# ") || !isHtml) {
94
+ const tmp = mkdtempSync(join(tmpdir(), "agentry-web-"));
95
+ const isMdc = url.endsWith(".mdc");
96
+ const isYaml = url.endsWith(".yaml") || url.endsWith(".yml");
97
+ let filename = "SKILL.md";
98
+ if (isMdc) filename = `${frontmatterName(content) || "agent"}.mdc`;
99
+ else if (isYaml) filename = "profile.yaml";
100
+ writeFileSync(join(tmp, filename), content);
101
+ return {
102
+ root: tmp,
103
+ cleanup: () => rmSync(tmp, {
104
+ recursive: true,
105
+ force: true
106
+ })
107
+ };
108
+ }
109
+ throw new Error(`Could not find a skill or git repository at ${url}`);
110
+ }
111
+ export { resolveWebSource };