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.
Files changed (108) hide show
  1. package/README.md +139 -208
  2. package/dist/index.d.ts +8 -2
  3. package/dist/index.js +4 -1
  4. package/dist/src/asset-spec.d.ts +2 -0
  5. package/dist/src/asset-spec.js +22 -3
  6. package/dist/src/asset-type-handler.d.ts +27 -0
  7. package/dist/src/asset-type-handler.js +33 -0
  8. package/dist/src/cli.js +201 -75
  9. package/dist/src/common.d.ts +6 -1
  10. package/dist/src/common.js +18 -4
  11. package/dist/src/config-cli.d.ts +9 -0
  12. package/dist/src/config-cli.js +473 -0
  13. package/dist/src/config.d.ts +19 -6
  14. package/dist/src/config.js +139 -29
  15. package/dist/src/db.d.ts +46 -0
  16. package/dist/src/db.js +299 -0
  17. package/dist/src/embedder.js +12 -7
  18. package/dist/src/github.d.ts +4 -0
  19. package/dist/src/github.js +19 -0
  20. package/dist/src/handlers/agent-handler.d.ts +2 -0
  21. package/dist/src/handlers/agent-handler.js +26 -0
  22. package/dist/src/handlers/command-handler.d.ts +2 -0
  23. package/dist/src/handlers/command-handler.js +23 -0
  24. package/dist/src/handlers/index.d.ts +6 -0
  25. package/dist/src/handlers/index.js +23 -0
  26. package/dist/src/handlers/knowledge-handler.d.ts +2 -0
  27. package/dist/src/handlers/knowledge-handler.js +56 -0
  28. package/dist/src/handlers/markdown-helpers.d.ts +7 -0
  29. package/dist/src/handlers/markdown-helpers.js +15 -0
  30. package/dist/src/handlers/script-handler.d.ts +2 -0
  31. package/dist/src/handlers/script-handler.js +78 -0
  32. package/dist/src/handlers/skill-handler.d.ts +2 -0
  33. package/dist/src/handlers/skill-handler.js +30 -0
  34. package/dist/src/handlers/tool-handler.d.ts +2 -0
  35. package/dist/src/handlers/tool-handler.js +58 -0
  36. package/dist/src/indexer.d.ts +1 -23
  37. package/dist/src/indexer.js +162 -155
  38. package/dist/src/init.d.ts +2 -2
  39. package/dist/src/init.js +21 -9
  40. package/dist/src/llm.js +4 -3
  41. package/dist/src/metadata.d.ts +0 -1
  42. package/dist/src/metadata.js +6 -64
  43. package/dist/src/origin-resolve.d.ts +19 -0
  44. package/dist/src/origin-resolve.js +53 -0
  45. package/dist/src/registry-install.d.ts +2 -2
  46. package/dist/src/registry-install.js +142 -35
  47. package/dist/src/registry-resolve.js +90 -22
  48. package/dist/src/registry-search.d.ts +22 -0
  49. package/dist/src/registry-search.js +231 -97
  50. package/dist/src/registry-types.d.ts +9 -2
  51. package/dist/src/stash-add.js +4 -4
  52. package/dist/src/stash-clone.d.ts +22 -0
  53. package/dist/src/stash-clone.js +83 -0
  54. package/dist/src/stash-ref.d.ts +27 -3
  55. package/dist/src/stash-ref.js +63 -24
  56. package/dist/src/stash-registry.js +12 -12
  57. package/dist/src/stash-resolve.js +3 -0
  58. package/dist/src/stash-search.js +168 -164
  59. package/dist/src/stash-show.d.ts +1 -1
  60. package/dist/src/stash-show.js +28 -96
  61. package/dist/src/stash-source.d.ts +24 -0
  62. package/dist/src/stash-source.js +81 -0
  63. package/dist/src/stash-types.d.ts +14 -4
  64. package/dist/src/stash.d.ts +6 -0
  65. package/dist/src/stash.js +3 -0
  66. package/dist/src/tool-runner.d.ts +1 -1
  67. package/dist/src/tool-runner.js +18 -5
  68. package/package.json +7 -2
  69. package/src/asset-spec.ts +20 -4
  70. package/src/asset-type-handler.ts +77 -0
  71. package/src/cli.ts +213 -82
  72. package/src/common.ts +23 -5
  73. package/src/config-cli.ts +499 -0
  74. package/src/config.ts +160 -38
  75. package/src/db.ts +411 -0
  76. package/src/embedder.ts +22 -11
  77. package/src/github.ts +21 -0
  78. package/src/handlers/agent-handler.ts +32 -0
  79. package/src/handlers/command-handler.ts +29 -0
  80. package/src/handlers/index.ts +25 -0
  81. package/src/handlers/knowledge-handler.ts +62 -0
  82. package/src/handlers/markdown-helpers.ts +19 -0
  83. package/src/handlers/script-handler.ts +92 -0
  84. package/src/handlers/skill-handler.ts +37 -0
  85. package/src/handlers/tool-handler.ts +71 -0
  86. package/src/indexer.ts +208 -187
  87. package/src/init.ts +17 -9
  88. package/src/llm.ts +4 -3
  89. package/src/metadata.ts +5 -65
  90. package/src/origin-resolve.ts +67 -0
  91. package/src/registry-install.ts +158 -42
  92. package/src/registry-resolve.ts +92 -23
  93. package/src/registry-search.ts +288 -98
  94. package/src/registry-types.ts +10 -2
  95. package/src/stash-add.ts +14 -17
  96. package/src/stash-clone.ts +127 -0
  97. package/src/stash-ref.ts +84 -26
  98. package/src/stash-registry.ts +12 -12
  99. package/src/stash-resolve.ts +3 -0
  100. package/src/stash-search.ts +202 -184
  101. package/src/stash-show.ts +33 -90
  102. package/src/stash-source.ts +103 -0
  103. package/src/stash-types.ts +14 -4
  104. package/src/stash.ts +8 -0
  105. package/src/tool-runner.ts +18 -5
  106. package/dist/src/similarity.d.ts +0 -34
  107. package/dist/src/similarity.js +0 -211
  108. 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, stashDir) {
34
- const current = loadConfig(stashDir);
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
- additionalStashDirs: syncedAdditional,
45
- registry: {
46
- installed: nextInstalled,
47
- },
75
+ registry: { installed: nextInstalled },
48
76
  };
49
- saveConfig(nextConfig, stashDir);
77
+ saveConfig(nextConfig);
50
78
  return nextConfig;
51
79
  }
52
- export function removeInstalledRegistryEntry(id, stashDir) {
53
- const current = loadConfig(stashDir);
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, stashDir);
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 fetch(url);
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
- const arrayBuffer = await response.arrayBuffer();
108
- fs.writeFileSync(destination, Buffer.from(arrayBuffer));
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
- const GITHUB_API_BASE = "https://api.github.com";
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 || name.includes(" ")) {
187
- throw new Error(`Invalid npm package name: \"${name}\".`);
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 githubHeaders() {
202
- const token = process.env.GITHUB_TOKEN?.trim();
203
- const headers = {
204
- Accept: "application/vnd.github+json",
205
- "User-Agent": "agentikit-registry",
206
- };
207
- if (token)
208
- headers.Authorization = `Bearer ${token}`;
209
- return headers;
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 fetch(url, { headers });
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 fetch(url, { headers });
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>;