agentikit 0.0.9 → 0.0.13
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/README.md +139 -208
- package/dist/index.d.ts +8 -2
- package/dist/index.js +4 -1
- package/dist/src/asset-spec.d.ts +2 -0
- package/dist/src/asset-spec.js +22 -3
- package/dist/src/asset-type-handler.d.ts +27 -0
- package/dist/src/asset-type-handler.js +33 -0
- package/dist/src/cli.js +201 -75
- package/dist/src/common.d.ts +6 -1
- package/dist/src/common.js +18 -4
- package/dist/src/config-cli.d.ts +9 -0
- package/dist/src/config-cli.js +473 -0
- package/dist/src/config.d.ts +19 -6
- package/dist/src/config.js +139 -29
- package/dist/src/db.d.ts +46 -0
- package/dist/src/db.js +299 -0
- package/dist/src/embedder.js +12 -7
- package/dist/src/github.d.ts +4 -0
- package/dist/src/github.js +19 -0
- package/dist/src/handlers/agent-handler.d.ts +2 -0
- package/dist/src/handlers/agent-handler.js +26 -0
- package/dist/src/handlers/command-handler.d.ts +2 -0
- package/dist/src/handlers/command-handler.js +23 -0
- package/dist/src/handlers/index.d.ts +6 -0
- package/dist/src/handlers/index.js +23 -0
- package/dist/src/handlers/knowledge-handler.d.ts +2 -0
- package/dist/src/handlers/knowledge-handler.js +56 -0
- package/dist/src/handlers/markdown-helpers.d.ts +7 -0
- package/dist/src/handlers/markdown-helpers.js +15 -0
- package/dist/src/handlers/script-handler.d.ts +2 -0
- package/dist/src/handlers/script-handler.js +78 -0
- package/dist/src/handlers/skill-handler.d.ts +2 -0
- package/dist/src/handlers/skill-handler.js +30 -0
- package/dist/src/handlers/tool-handler.d.ts +2 -0
- package/dist/src/handlers/tool-handler.js +58 -0
- package/dist/src/indexer.d.ts +1 -23
- package/dist/src/indexer.js +162 -155
- package/dist/src/init.d.ts +2 -2
- package/dist/src/init.js +21 -9
- package/dist/src/llm.js +4 -3
- package/dist/src/metadata.d.ts +0 -1
- package/dist/src/metadata.js +6 -64
- package/dist/src/origin-resolve.d.ts +19 -0
- package/dist/src/origin-resolve.js +53 -0
- package/dist/src/registry-install.d.ts +2 -2
- package/dist/src/registry-install.js +142 -35
- package/dist/src/registry-resolve.js +90 -22
- package/dist/src/registry-search.d.ts +22 -0
- package/dist/src/registry-search.js +231 -97
- package/dist/src/registry-types.d.ts +9 -2
- package/dist/src/stash-add.js +4 -4
- package/dist/src/stash-clone.d.ts +22 -0
- package/dist/src/stash-clone.js +83 -0
- package/dist/src/stash-ref.d.ts +27 -3
- package/dist/src/stash-ref.js +63 -24
- package/dist/src/stash-registry.js +12 -12
- package/dist/src/stash-resolve.js +3 -0
- package/dist/src/stash-search.js +168 -164
- package/dist/src/stash-show.d.ts +1 -1
- package/dist/src/stash-show.js +28 -96
- package/dist/src/stash-source.d.ts +24 -0
- package/dist/src/stash-source.js +81 -0
- package/dist/src/stash-types.d.ts +14 -4
- package/dist/src/stash.d.ts +6 -0
- package/dist/src/stash.js +3 -0
- package/dist/src/tool-runner.d.ts +1 -1
- package/dist/src/tool-runner.js +18 -5
- package/package.json +7 -2
- package/src/asset-spec.ts +20 -4
- package/src/asset-type-handler.ts +77 -0
- package/src/cli.ts +213 -82
- package/src/common.ts +23 -5
- package/src/config-cli.ts +499 -0
- package/src/config.ts +160 -38
- package/src/db.ts +411 -0
- package/src/embedder.ts +22 -11
- package/src/github.ts +21 -0
- package/src/handlers/agent-handler.ts +32 -0
- package/src/handlers/command-handler.ts +29 -0
- package/src/handlers/index.ts +25 -0
- package/src/handlers/knowledge-handler.ts +62 -0
- package/src/handlers/markdown-helpers.ts +19 -0
- package/src/handlers/script-handler.ts +92 -0
- package/src/handlers/skill-handler.ts +37 -0
- package/src/handlers/tool-handler.ts +71 -0
- package/src/indexer.ts +208 -187
- package/src/init.ts +17 -9
- package/src/llm.ts +4 -3
- package/src/metadata.ts +5 -65
- package/src/origin-resolve.ts +67 -0
- package/src/registry-install.ts +158 -42
- package/src/registry-resolve.ts +92 -23
- package/src/registry-search.ts +288 -98
- package/src/registry-types.ts +10 -2
- package/src/stash-add.ts +14 -17
- package/src/stash-clone.ts +127 -0
- package/src/stash-ref.ts +84 -26
- package/src/stash-registry.ts +12 -12
- package/src/stash-resolve.ts +3 -0
- package/src/stash-search.ts +202 -184
- package/src/stash-show.ts +33 -90
- package/src/stash-source.ts +103 -0
- package/src/stash-types.ts +14 -4
- package/src/stash.ts +8 -0
- package/src/tool-runner.ts +18 -5
- package/dist/src/similarity.d.ts +0 -34
- package/dist/src/similarity.js +0 -211
- package/src/similarity.ts +0 -271
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { TYPE_DIRS } from "./common";
|
|
4
|
+
import { fetchWithTimeout, isWithin, TYPE_DIRS } from "./common";
|
|
5
5
|
import { loadConfig, saveConfig } from "./config";
|
|
6
6
|
import { parseRegistryRef, resolveRegistryArtifact } from "./registry-resolve";
|
|
7
7
|
const REGISTRY_STASH_DIR_NAMES = new Set(Object.values(TYPE_DIRS));
|
|
8
8
|
export async function installRegistryRef(ref, options) {
|
|
9
9
|
const parsed = parseRegistryRef(ref);
|
|
10
|
+
if (parsed.source === "git") {
|
|
11
|
+
return installGitRegistryRef(parsed, options);
|
|
12
|
+
}
|
|
10
13
|
const resolved = await resolveRegistryArtifact(parsed);
|
|
11
14
|
const installedAt = (options?.now ?? new Date()).toISOString();
|
|
12
15
|
const cacheRootDir = options?.cacheRootDir ?? getRegistryCacheRootDir();
|
|
@@ -16,6 +19,38 @@ export async function installRegistryRef(ref, options) {
|
|
|
16
19
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
17
20
|
await downloadArchive(resolved.artifactUrl, archivePath);
|
|
18
21
|
extractTarGzSecure(archivePath, extractedDir);
|
|
22
|
+
const provisionalKitRoot = detectStashRoot(extractedDir);
|
|
23
|
+
const installRoot = applyAgentikitIncludeConfig(provisionalKitRoot, cacheDir, extractedDir) ?? provisionalKitRoot;
|
|
24
|
+
const stashRoot = detectStashRoot(installRoot);
|
|
25
|
+
return {
|
|
26
|
+
id: resolved.id,
|
|
27
|
+
source: resolved.source,
|
|
28
|
+
ref: resolved.ref,
|
|
29
|
+
artifactUrl: resolved.artifactUrl,
|
|
30
|
+
resolvedVersion: resolved.resolvedVersion,
|
|
31
|
+
resolvedRevision: resolved.resolvedRevision,
|
|
32
|
+
installedAt,
|
|
33
|
+
cacheDir,
|
|
34
|
+
extractedDir,
|
|
35
|
+
stashRoot,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async function installGitRegistryRef(parsed, options) {
|
|
39
|
+
const resolved = await resolveRegistryArtifact(parsed);
|
|
40
|
+
const installedAt = (options?.now ?? new Date()).toISOString();
|
|
41
|
+
const cacheRootDir = options?.cacheRootDir ?? getRegistryCacheRootDir();
|
|
42
|
+
const cacheDir = buildInstallCacheDir(cacheRootDir, parsed.source, parsed.id);
|
|
43
|
+
const extractedDir = path.join(cacheDir, "extracted");
|
|
44
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
45
|
+
fs.rmSync(extractedDir, { recursive: true, force: true });
|
|
46
|
+
fs.mkdirSync(extractedDir, { recursive: true });
|
|
47
|
+
const includeConfig = findNearestAgentikitIncludeConfig(parsed.sourcePath, parsed.repoRoot);
|
|
48
|
+
if (includeConfig) {
|
|
49
|
+
copyIncludedPaths(includeConfig.baseDir, includeConfig.include, extractedDir);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
copyDirectoryContents(parsed.sourcePath, extractedDir);
|
|
53
|
+
}
|
|
19
54
|
const stashRoot = detectStashRoot(extractedDir);
|
|
20
55
|
return {
|
|
21
56
|
id: resolved.id,
|
|
@@ -30,39 +65,27 @@ export async function installRegistryRef(ref, options) {
|
|
|
30
65
|
stashRoot,
|
|
31
66
|
};
|
|
32
67
|
}
|
|
33
|
-
export function upsertInstalledRegistryEntry(entry
|
|
34
|
-
const current = loadConfig(
|
|
68
|
+
export function upsertInstalledRegistryEntry(entry) {
|
|
69
|
+
const current = loadConfig();
|
|
35
70
|
const currentInstalled = current.registry?.installed ?? [];
|
|
36
|
-
const previousRegistryRoots = new Set(currentInstalled.map((item) => path.resolve(item.stashRoot)));
|
|
37
71
|
const withoutExisting = currentInstalled.filter((item) => item.id !== entry.id);
|
|
38
72
|
const nextInstalled = [...withoutExisting, normalizeInstalledEntry(entry)];
|
|
39
|
-
const nextRegistryRoots = new Set(nextInstalled.map((item) => path.resolve(item.stashRoot)));
|
|
40
|
-
const preservedAdditional = current.additionalStashDirs.filter((dir) => !previousRegistryRoots.has(path.resolve(dir)));
|
|
41
|
-
const syncedAdditional = uniquePaths([...preservedAdditional, ...nextRegistryRoots]);
|
|
42
73
|
const nextConfig = {
|
|
43
74
|
...current,
|
|
44
|
-
|
|
45
|
-
registry: {
|
|
46
|
-
installed: nextInstalled,
|
|
47
|
-
},
|
|
75
|
+
registry: { installed: nextInstalled },
|
|
48
76
|
};
|
|
49
|
-
saveConfig(nextConfig
|
|
77
|
+
saveConfig(nextConfig);
|
|
50
78
|
return nextConfig;
|
|
51
79
|
}
|
|
52
|
-
export function removeInstalledRegistryEntry(id
|
|
53
|
-
const current = loadConfig(
|
|
80
|
+
export function removeInstalledRegistryEntry(id) {
|
|
81
|
+
const current = loadConfig();
|
|
54
82
|
const currentInstalled = current.registry?.installed ?? [];
|
|
55
|
-
const previousRegistryRoots = new Set(currentInstalled.map((item) => path.resolve(item.stashRoot)));
|
|
56
83
|
const nextInstalled = currentInstalled.filter((item) => item.id !== id);
|
|
57
|
-
const nextRegistryRoots = new Set(nextInstalled.map((item) => path.resolve(item.stashRoot)));
|
|
58
|
-
const preservedAdditional = current.additionalStashDirs.filter((dir) => !previousRegistryRoots.has(path.resolve(dir)));
|
|
59
|
-
const syncedAdditional = uniquePaths([...preservedAdditional, ...nextRegistryRoots]);
|
|
60
84
|
const nextConfig = {
|
|
61
85
|
...current,
|
|
62
|
-
additionalStashDirs: syncedAdditional,
|
|
63
86
|
registry: nextInstalled.length > 0 ? { installed: nextInstalled } : undefined,
|
|
64
87
|
};
|
|
65
|
-
saveConfig(nextConfig
|
|
88
|
+
saveConfig(nextConfig);
|
|
66
89
|
return nextConfig;
|
|
67
90
|
}
|
|
68
91
|
export function getRegistryCacheRootDir() {
|
|
@@ -99,13 +122,32 @@ function buildInstallCacheDir(cacheRootDir, source, id) {
|
|
|
99
122
|
const stamp = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
100
123
|
return path.join(cacheRootDir, slug || source, stamp);
|
|
101
124
|
}
|
|
125
|
+
function applyAgentikitIncludeConfig(sourceRoot, cacheDir, searchRoot = sourceRoot) {
|
|
126
|
+
const includeConfig = findNearestAgentikitIncludeConfig(sourceRoot, searchRoot);
|
|
127
|
+
if (!includeConfig)
|
|
128
|
+
return undefined;
|
|
129
|
+
const selectedDir = path.join(cacheDir, "selected");
|
|
130
|
+
fs.rmSync(selectedDir, { recursive: true, force: true });
|
|
131
|
+
fs.mkdirSync(selectedDir, { recursive: true });
|
|
132
|
+
copyIncludedPaths(includeConfig.baseDir, includeConfig.include, selectedDir);
|
|
133
|
+
return selectedDir;
|
|
134
|
+
}
|
|
102
135
|
async function downloadArchive(url, destination) {
|
|
103
|
-
const response = await
|
|
136
|
+
const response = await fetchWithTimeout(url, undefined, 120_000);
|
|
104
137
|
if (!response.ok) {
|
|
105
138
|
throw new Error(`Failed to download archive (${response.status}) from ${url}`);
|
|
106
139
|
}
|
|
107
|
-
|
|
108
|
-
|
|
140
|
+
// Stream response to disk instead of buffering the entire archive in memory.
|
|
141
|
+
// Uses Bun.write which handles Response streaming natively.
|
|
142
|
+
const BunRuntime = globalThis.Bun;
|
|
143
|
+
if (BunRuntime?.write) {
|
|
144
|
+
await BunRuntime.write(destination, response);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// Fallback for non-Bun environments (e.g., tests)
|
|
148
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
149
|
+
fs.writeFileSync(destination, Buffer.from(arrayBuffer));
|
|
150
|
+
}
|
|
109
151
|
}
|
|
110
152
|
function extractTarGzSecure(archivePath, destinationDir) {
|
|
111
153
|
const listResult = spawnSync("tar", ["tzf", archivePath], { encoding: "utf8" });
|
|
@@ -156,6 +198,83 @@ function isDirectory(target) {
|
|
|
156
198
|
return false;
|
|
157
199
|
}
|
|
158
200
|
}
|
|
201
|
+
function readAgentikitIncludeConfigAtDir(dirPath) {
|
|
202
|
+
const packageJsonPath = path.join(dirPath, "package.json");
|
|
203
|
+
if (!fs.existsSync(packageJsonPath))
|
|
204
|
+
return undefined;
|
|
205
|
+
let pkg;
|
|
206
|
+
try {
|
|
207
|
+
pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
if (typeof pkg !== "object" || pkg === null || Array.isArray(pkg))
|
|
213
|
+
return undefined;
|
|
214
|
+
const agentikit = pkg.agentikit;
|
|
215
|
+
if (typeof agentikit !== "object" || agentikit === null || Array.isArray(agentikit))
|
|
216
|
+
return undefined;
|
|
217
|
+
const include = agentikit.include;
|
|
218
|
+
if (!Array.isArray(include))
|
|
219
|
+
return undefined;
|
|
220
|
+
const parsedInclude = include
|
|
221
|
+
.filter((value) => typeof value === "string")
|
|
222
|
+
.map((value) => value.trim())
|
|
223
|
+
.filter(Boolean);
|
|
224
|
+
return parsedInclude.length > 0 ? { baseDir: dirPath, include: parsedInclude } : undefined;
|
|
225
|
+
}
|
|
226
|
+
function findNearestAgentikitIncludeConfig(startDir, stopDir) {
|
|
227
|
+
let current = path.resolve(startDir);
|
|
228
|
+
const boundary = path.resolve(stopDir);
|
|
229
|
+
while (isWithin(current, boundary)) {
|
|
230
|
+
const config = readAgentikitIncludeConfigAtDir(current);
|
|
231
|
+
if (config)
|
|
232
|
+
return config;
|
|
233
|
+
if (current === boundary)
|
|
234
|
+
break;
|
|
235
|
+
const parent = path.dirname(current);
|
|
236
|
+
if (parent === current)
|
|
237
|
+
break;
|
|
238
|
+
current = parent;
|
|
239
|
+
}
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
function copyIncludedPaths(baseDir, include, destinationDir) {
|
|
243
|
+
for (const entry of include) {
|
|
244
|
+
const resolvedSource = path.resolve(baseDir, entry);
|
|
245
|
+
if (!isWithin(resolvedSource, baseDir)) {
|
|
246
|
+
throw new Error(`Path in agentikit.include escapes the package root: ${entry}`);
|
|
247
|
+
}
|
|
248
|
+
if (!fs.existsSync(resolvedSource)) {
|
|
249
|
+
throw new Error(`Path in agentikit.include does not exist: ${entry}`);
|
|
250
|
+
}
|
|
251
|
+
if (path.basename(resolvedSource) === ".git") {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const relativePath = path.relative(baseDir, resolvedSource);
|
|
255
|
+
if (!relativePath || relativePath === ".") {
|
|
256
|
+
copyDirectoryContents(baseDir, destinationDir);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
copyPath(resolvedSource, path.join(destinationDir, relativePath));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function copyDirectoryContents(sourceDir, destinationDir) {
|
|
263
|
+
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
264
|
+
if (entry.name === ".git")
|
|
265
|
+
continue;
|
|
266
|
+
copyPath(path.join(sourceDir, entry.name), path.join(destinationDir, entry.name));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function copyPath(sourcePath, destinationPath) {
|
|
270
|
+
const stat = fs.statSync(sourcePath);
|
|
271
|
+
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
272
|
+
if (stat.isDirectory()) {
|
|
273
|
+
fs.cpSync(sourcePath, destinationPath, { recursive: true, force: true });
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
fs.copyFileSync(sourcePath, destinationPath);
|
|
277
|
+
}
|
|
159
278
|
function hasStashDirs(dirPath) {
|
|
160
279
|
if (!isDirectory(dirPath))
|
|
161
280
|
return false;
|
|
@@ -194,15 +313,3 @@ function normalizeInstalledEntry(entry) {
|
|
|
194
313
|
cacheDir: path.resolve(entry.cacheDir),
|
|
195
314
|
};
|
|
196
315
|
}
|
|
197
|
-
function uniquePaths(paths) {
|
|
198
|
-
const seen = new Set();
|
|
199
|
-
const result = [];
|
|
200
|
-
for (const candidate of paths) {
|
|
201
|
-
const normalized = path.resolve(candidate);
|
|
202
|
-
if (seen.has(normalized))
|
|
203
|
-
continue;
|
|
204
|
-
seen.add(normalized);
|
|
205
|
-
result.push(normalized);
|
|
206
|
-
}
|
|
207
|
-
return result;
|
|
208
|
-
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { fetchWithTimeout } from "./common";
|
|
6
|
+
import { GITHUB_API_BASE, githubHeaders, asRecord, asString } from "./github";
|
|
2
7
|
export function parseRegistryRef(rawRef) {
|
|
3
8
|
const ref = rawRef.trim();
|
|
4
9
|
if (!ref)
|
|
@@ -12,6 +17,10 @@ export function parseRegistryRef(rawRef) {
|
|
|
12
17
|
if (ref.startsWith("http://") || ref.startsWith("https://")) {
|
|
13
18
|
return parseGithubUrl(ref);
|
|
14
19
|
}
|
|
20
|
+
const localGitRef = tryParseLocalGitRef(ref, isPathLikeRef(ref));
|
|
21
|
+
if (localGitRef) {
|
|
22
|
+
return localGitRef;
|
|
23
|
+
}
|
|
15
24
|
if (ref.startsWith("@") || !looksLikeGithubOwnerRepo(ref)) {
|
|
16
25
|
return parseNpmRef(ref, ref);
|
|
17
26
|
}
|
|
@@ -21,6 +30,9 @@ export async function resolveRegistryArtifact(parsed) {
|
|
|
21
30
|
if (parsed.source === "npm") {
|
|
22
31
|
return resolveNpmArtifact(parsed);
|
|
23
32
|
}
|
|
33
|
+
if (parsed.source === "git") {
|
|
34
|
+
return resolveGitArtifact(parsed);
|
|
35
|
+
}
|
|
24
36
|
return resolveGithubArtifact(parsed);
|
|
25
37
|
}
|
|
26
38
|
function parseNpmRef(input, originalRef) {
|
|
@@ -84,6 +96,41 @@ function parseGithubUrl(rawUrl) {
|
|
|
84
96
|
requestedRef,
|
|
85
97
|
};
|
|
86
98
|
}
|
|
99
|
+
function tryParseLocalGitRef(rawRef, explicitPath) {
|
|
100
|
+
if (!explicitPath) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
const resolvedPath = path.resolve(rawRef);
|
|
104
|
+
let stat;
|
|
105
|
+
try {
|
|
106
|
+
stat = fs.statSync(resolvedPath);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
throw new Error(`Local path not found: ${resolvedPath}`);
|
|
110
|
+
}
|
|
111
|
+
if (!stat.isDirectory()) {
|
|
112
|
+
throw new Error("Local add path must be a directory, but the provided path is not one.");
|
|
113
|
+
}
|
|
114
|
+
const repoRoot = findGitRepoRoot(resolvedPath);
|
|
115
|
+
if (!repoRoot) {
|
|
116
|
+
throw new Error("Local add path must be inside a git repository.");
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
source: "git",
|
|
120
|
+
ref: rawRef,
|
|
121
|
+
id: `git:${encodeURIComponent(resolvedPath)}`,
|
|
122
|
+
repoRoot,
|
|
123
|
+
sourcePath: resolvedPath,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function isPathLikeRef(ref) {
|
|
127
|
+
if (path.isAbsolute(ref))
|
|
128
|
+
return true;
|
|
129
|
+
if (ref.startsWith("./") || ref.startsWith("../") || ref.startsWith(".\\") || ref.startsWith("..\\")) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
return ref.includes("/") || ref.includes("\\");
|
|
133
|
+
}
|
|
87
134
|
async function resolveNpmArtifact(parsed) {
|
|
88
135
|
const encodedName = encodeURIComponent(parsed.packageName);
|
|
89
136
|
const metadata = await fetchJson(`https://registry.npmjs.org/${encodedName}`);
|
|
@@ -162,6 +209,16 @@ async function resolveGithubArtifact(parsed) {
|
|
|
162
209
|
resolvedRevision: asString(commit?.sha) ?? defaultBranch,
|
|
163
210
|
};
|
|
164
211
|
}
|
|
212
|
+
async function resolveGitArtifact(parsed) {
|
|
213
|
+
return {
|
|
214
|
+
id: parsed.id,
|
|
215
|
+
source: parsed.source,
|
|
216
|
+
ref: parsed.ref,
|
|
217
|
+
artifactUrl: pathToFileURL(parsed.sourcePath).toString(),
|
|
218
|
+
resolvedRevision: readGitValue(parsed.repoRoot, "rev-parse", "HEAD"),
|
|
219
|
+
resolvedVersion: readGitValue(parsed.repoRoot, "rev-parse", "--abbrev-ref", "HEAD"),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
165
222
|
function splitNpmNameAndVersion(input) {
|
|
166
223
|
if (input.startsWith("@")) {
|
|
167
224
|
const secondAt = input.indexOf("@", 1);
|
|
@@ -183,8 +240,18 @@ function splitNpmNameAndVersion(input) {
|
|
|
183
240
|
return { packageName: input };
|
|
184
241
|
}
|
|
185
242
|
function validateNpmPackageName(name) {
|
|
186
|
-
if (!name
|
|
187
|
-
throw new Error(
|
|
243
|
+
if (!name)
|
|
244
|
+
throw new Error('Invalid npm package name: name is required.');
|
|
245
|
+
if (name.length > 214)
|
|
246
|
+
throw new Error(`Invalid npm package name: "${name}" exceeds 214 characters.`);
|
|
247
|
+
if (name !== name.toLowerCase() && !name.startsWith('@')) {
|
|
248
|
+
throw new Error(`Invalid npm package name: "${name}" must be lowercase.`);
|
|
249
|
+
}
|
|
250
|
+
if (name.startsWith('.') || name.startsWith('_')) {
|
|
251
|
+
throw new Error(`Invalid npm package name: "${name}" cannot start with . or _.`);
|
|
252
|
+
}
|
|
253
|
+
if (/[~'!()*]/.test(name) || name.includes(' ') || encodeURIComponent(name).replace(/%40/g, '@').replace(/%2[Ff]/g, '/') !== name) {
|
|
254
|
+
throw new Error(`Invalid npm package name: "${name}" contains invalid characters.`);
|
|
188
255
|
}
|
|
189
256
|
}
|
|
190
257
|
function looksLikeGithubOwnerRepo(ref) {
|
|
@@ -198,34 +265,35 @@ function splitRefSuffix(value) {
|
|
|
198
265
|
return [value, undefined];
|
|
199
266
|
return [value.slice(0, hash), value.slice(hash + 1) || undefined];
|
|
200
267
|
}
|
|
201
|
-
function
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
268
|
+
function findGitRepoRoot(startDir) {
|
|
269
|
+
let current = path.resolve(startDir);
|
|
270
|
+
while (true) {
|
|
271
|
+
if (fs.existsSync(path.join(current, ".git"))) {
|
|
272
|
+
return current;
|
|
273
|
+
}
|
|
274
|
+
const parent = path.dirname(current);
|
|
275
|
+
if (parent === current)
|
|
276
|
+
return undefined;
|
|
277
|
+
current = parent;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function readGitValue(repoRoot, ...args) {
|
|
281
|
+
const result = spawnSync("git", ["-C", repoRoot, ...args], { encoding: "utf8" });
|
|
282
|
+
if (result.status !== 0)
|
|
283
|
+
return undefined;
|
|
284
|
+
const value = result.stdout.trim();
|
|
285
|
+
return value || undefined;
|
|
210
286
|
}
|
|
211
287
|
async function fetchJson(url, headers) {
|
|
212
|
-
const response = await
|
|
288
|
+
const response = await fetchWithTimeout(url, { headers });
|
|
213
289
|
if (!response.ok) {
|
|
214
290
|
throw new Error(`Request failed (${response.status}) for ${url}`);
|
|
215
291
|
}
|
|
216
292
|
return await response.json();
|
|
217
293
|
}
|
|
218
294
|
async function tryFetchJson(url, headers) {
|
|
219
|
-
const response = await
|
|
295
|
+
const response = await fetchWithTimeout(url, { headers });
|
|
220
296
|
if (!response.ok)
|
|
221
297
|
return null;
|
|
222
298
|
return await response.json();
|
|
223
299
|
}
|
|
224
|
-
function asRecord(value) {
|
|
225
|
-
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
226
|
-
? value
|
|
227
|
-
: {};
|
|
228
|
-
}
|
|
229
|
-
function asString(value) {
|
|
230
|
-
return typeof value === "string" && value ? value : undefined;
|
|
231
|
-
}
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
import type { RegistrySearchResponse } from "./registry-types";
|
|
2
|
+
export interface RegistryIndex {
|
|
3
|
+
version: number;
|
|
4
|
+
updatedAt: string;
|
|
5
|
+
kits: RegistryKitEntry[];
|
|
6
|
+
}
|
|
7
|
+
export interface RegistryKitEntry {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
ref: string;
|
|
12
|
+
source: "npm" | "github" | "git";
|
|
13
|
+
homepage?: string;
|
|
14
|
+
tags?: string[];
|
|
15
|
+
assetTypes?: string[];
|
|
16
|
+
author?: string;
|
|
17
|
+
license?: string;
|
|
18
|
+
latestVersion?: string;
|
|
19
|
+
/** Whether this entry was manually reviewed and approved */
|
|
20
|
+
curated?: boolean;
|
|
21
|
+
}
|
|
2
22
|
export interface RegistrySearchOptions {
|
|
3
23
|
limit?: number;
|
|
24
|
+
/** Override registry URL(s). Accepts a single URL or an array. */
|
|
25
|
+
registryUrls?: string | string[];
|
|
4
26
|
}
|
|
5
27
|
export declare function searchRegistry(query: string, options?: RegistrySearchOptions): Promise<RegistrySearchResponse>;
|