agentpacks 0.3.0 → 0.4.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/README.md +168 -8
- package/dist/api.d.ts +2 -0
- package/dist/api.js +929 -409
- package/dist/cli/export-cmd.js +281 -149
- package/dist/cli/generate.js +740 -247
- package/dist/cli/import-cmd.js +57 -85
- package/dist/cli/info.d.ts +4 -0
- package/dist/cli/info.js +232 -0
- package/dist/cli/init.js +8 -36
- package/dist/cli/install.js +414 -129
- package/dist/cli/login.d.ts +9 -0
- package/dist/cli/login.js +202 -0
- package/dist/cli/models-explain.d.ts +16 -0
- package/dist/cli/models-explain.js +1205 -0
- package/dist/cli/pack/create.js +4 -32
- package/dist/cli/pack/enable.js +1 -29
- package/dist/cli/pack/list.js +266 -134
- package/dist/cli/pack/validate.js +274 -127
- package/dist/cli/publish.d.ts +8 -0
- package/dist/cli/publish.js +672 -0
- package/dist/cli/search.d.ts +12 -0
- package/dist/cli/search.js +210 -0
- package/dist/core/config.d.ts +2 -1
- package/dist/core/config.js +74 -117
- package/dist/core/dependency-resolver.js +4 -28
- package/dist/core/feature-merger.d.ts +7 -0
- package/dist/core/feature-merger.js +289 -29
- package/dist/core/index.js +283 -140
- package/dist/core/lockfile.js +0 -28
- package/dist/core/metarepo.js +74 -116
- package/dist/core/pack-loader.d.ts +2 -0
- package/dist/core/pack-loader.js +266 -133
- package/dist/core/profile-resolver.d.ts +75 -0
- package/dist/core/profile-resolver.js +111 -0
- package/dist/exporters/cursor-plugin.js +4 -32
- package/dist/exporters/index.js +4 -32
- package/dist/features/agents.d.ts +5 -0
- package/dist/features/agents.js +2 -30
- package/dist/features/commands.js +2 -30
- package/dist/features/hooks.js +2 -30
- package/dist/features/ignore.js +0 -28
- package/dist/features/index.d.ts +1 -0
- package/dist/features/index.js +176 -31
- package/dist/features/mcp.js +2 -30
- package/dist/features/models.d.ts +167 -0
- package/dist/features/models.js +293 -0
- package/dist/features/plugins.js +2 -30
- package/dist/features/rules.js +2 -30
- package/dist/features/skills.js +2 -30
- package/dist/importers/claude-code.js +10 -38
- package/dist/importers/cursor.js +15 -43
- package/dist/importers/opencode.js +16 -44
- package/dist/importers/rulesync.js +22 -50
- package/dist/index.js +1710 -538
- package/dist/node/api.js +929 -409
- package/dist/node/cli/export-cmd.js +281 -149
- package/dist/node/cli/generate.js +740 -247
- package/dist/node/cli/import-cmd.js +57 -85
- package/dist/node/cli/info.js +232 -0
- package/dist/node/cli/init.js +8 -36
- package/dist/node/cli/install.js +414 -129
- package/dist/node/cli/login.js +202 -0
- package/dist/node/cli/models-explain.js +1205 -0
- package/dist/node/cli/pack/create.js +4 -32
- package/dist/node/cli/pack/enable.js +1 -29
- package/dist/node/cli/pack/list.js +266 -134
- package/dist/node/cli/pack/validate.js +274 -127
- package/dist/node/cli/publish.js +672 -0
- package/dist/node/cli/search.js +210 -0
- package/dist/node/core/config.js +74 -117
- package/dist/node/core/dependency-resolver.js +4 -28
- package/dist/node/core/feature-merger.js +289 -29
- package/dist/node/core/index.js +283 -140
- package/dist/node/core/lockfile.js +0 -28
- package/dist/node/core/metarepo.js +74 -116
- package/dist/node/core/pack-loader.js +266 -133
- package/dist/node/core/profile-resolver.js +111 -0
- package/dist/node/exporters/cursor-plugin.js +4 -32
- package/dist/node/exporters/index.js +4 -32
- package/dist/node/features/agents.js +2 -30
- package/dist/node/features/commands.js +2 -30
- package/dist/node/features/hooks.js +2 -30
- package/dist/node/features/ignore.js +0 -28
- package/dist/node/features/index.js +176 -31
- package/dist/node/features/mcp.js +2 -30
- package/dist/node/features/models.js +293 -0
- package/dist/node/features/plugins.js +2 -30
- package/dist/node/features/rules.js +2 -30
- package/dist/node/features/skills.js +2 -30
- package/dist/node/importers/claude-code.js +10 -38
- package/dist/node/importers/cursor.js +15 -43
- package/dist/node/importers/opencode.js +16 -44
- package/dist/node/importers/rulesync.js +22 -50
- package/dist/node/index.js +1710 -538
- package/dist/node/sources/git-ref.js +7 -30
- package/dist/node/sources/git.js +7 -30
- package/dist/node/sources/index.js +337 -39
- package/dist/node/sources/local.js +0 -28
- package/dist/node/sources/npm-ref.js +0 -28
- package/dist/node/sources/npm.js +10 -37
- package/dist/node/sources/registry-ref.js +37 -0
- package/dist/node/sources/registry.js +355 -0
- package/dist/node/targets/additional-targets.js +196 -37
- package/dist/node/targets/agents-md.js +5 -33
- package/dist/node/targets/base-target.js +0 -28
- package/dist/node/targets/claude-code.js +211 -41
- package/dist/node/targets/codex-cli.js +7 -35
- package/dist/node/targets/copilot.js +202 -41
- package/dist/node/targets/cursor.js +188 -40
- package/dist/node/targets/gemini-cli.js +10 -38
- package/dist/node/targets/generic-md-target.js +196 -37
- package/dist/node/targets/index.js +414 -106
- package/dist/node/targets/opencode.js +171 -51
- package/dist/node/targets/registry.js +414 -106
- package/dist/node/utils/credentials.js +38 -0
- package/dist/node/utils/diff.js +22 -34
- package/dist/node/utils/filesystem.js +2 -30
- package/dist/node/utils/frontmatter.js +0 -28
- package/dist/node/utils/global.js +3 -31
- package/dist/node/utils/markdown.js +0 -28
- package/dist/node/utils/model-allowlist.js +110 -0
- package/dist/node/utils/model-guidance.js +78 -0
- package/dist/node/utils/registry-client.js +142 -0
- package/dist/node/utils/tarball.js +49 -0
- package/dist/sources/git-ref.js +7 -30
- package/dist/sources/git.d.ts +2 -2
- package/dist/sources/git.js +7 -30
- package/dist/sources/index.d.ts +2 -0
- package/dist/sources/index.js +337 -39
- package/dist/sources/local.js +0 -28
- package/dist/sources/npm-ref.js +0 -28
- package/dist/sources/npm.js +10 -37
- package/dist/sources/registry-ref.d.ts +30 -0
- package/dist/sources/registry-ref.js +37 -0
- package/dist/sources/registry.d.ts +18 -0
- package/dist/sources/registry.js +355 -0
- package/dist/targets/additional-targets.js +196 -37
- package/dist/targets/agents-md.js +5 -33
- package/dist/targets/base-target.d.ts +2 -0
- package/dist/targets/base-target.js +0 -28
- package/dist/targets/claude-code.js +211 -41
- package/dist/targets/codex-cli.js +7 -35
- package/dist/targets/copilot.js +202 -41
- package/dist/targets/cursor.js +188 -40
- package/dist/targets/gemini-cli.js +10 -38
- package/dist/targets/generic-md-target.js +196 -37
- package/dist/targets/index.js +414 -106
- package/dist/targets/opencode.js +171 -51
- package/dist/targets/registry.js +414 -106
- package/dist/utils/credentials.d.ts +19 -0
- package/dist/utils/credentials.js +38 -0
- package/dist/utils/diff.js +22 -34
- package/dist/utils/filesystem.js +2 -30
- package/dist/utils/frontmatter.js +0 -28
- package/dist/utils/global.js +3 -31
- package/dist/utils/markdown.js +0 -28
- package/dist/utils/model-allowlist.d.ts +39 -0
- package/dist/utils/model-allowlist.js +110 -0
- package/dist/utils/model-guidance.d.ts +6 -0
- package/dist/utils/model-guidance.js +78 -0
- package/dist/utils/registry-client.d.ts +141 -0
- package/dist/utils/registry-client.js +142 -0
- package/dist/utils/tarball.d.ts +13 -0
- package/dist/utils/tarball.js +49 -0
- package/package.json +171 -5
- package/templates/pack/models.json +38 -0
package/dist/node/sources/npm.js
CHANGED
|
@@ -1,32 +1,4 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
|
-
var __toCommonJS = (from) => {
|
|
8
|
-
var entry = __moduleCache.get(from), desc;
|
|
9
|
-
if (entry)
|
|
10
|
-
return entry;
|
|
11
|
-
entry = __defProp({}, "__esModule", { value: true });
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
13
|
-
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
14
|
-
get: () => from[key],
|
|
15
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
-
}));
|
|
17
|
-
__moduleCache.set(from, entry);
|
|
18
|
-
return entry;
|
|
19
|
-
};
|
|
20
|
-
var __export = (target, all) => {
|
|
21
|
-
for (var name in all)
|
|
22
|
-
__defProp(target, name, {
|
|
23
|
-
get: all[name],
|
|
24
|
-
enumerable: true,
|
|
25
|
-
configurable: true,
|
|
26
|
-
set: (newValue) => all[name] = () => newValue
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
30
2
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
31
3
|
|
|
32
4
|
// src/core/lockfile.ts
|
|
@@ -113,7 +85,7 @@ function npmSourceKey(parsed) {
|
|
|
113
85
|
}
|
|
114
86
|
|
|
115
87
|
// src/sources/npm.ts
|
|
116
|
-
import { mkdirSync, existsSync as existsSync2 } from "fs";
|
|
88
|
+
import { mkdirSync, existsSync as existsSync2, readdirSync } from "fs";
|
|
117
89
|
import { resolve as resolve2, join } from "path";
|
|
118
90
|
import { execSync } from "child_process";
|
|
119
91
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
@@ -138,18 +110,15 @@ async function installNpmSource(projectRoot, source, lockfile, options = {}) {
|
|
|
138
110
|
throw new Error(`Frozen mode: no lockfile entry for source "${sourceKey}".`);
|
|
139
111
|
}
|
|
140
112
|
let resolvedVersion;
|
|
141
|
-
let tarballUrl;
|
|
142
113
|
if (locked && !options.update) {
|
|
143
114
|
resolvedVersion = locked.resolvedRef;
|
|
144
|
-
tarballUrl = "";
|
|
145
115
|
} else {
|
|
146
116
|
const resolved = await resolveNpmVersion(parsed);
|
|
147
117
|
resolvedVersion = resolved.version;
|
|
148
|
-
tarballUrl = resolved.tarball;
|
|
149
118
|
}
|
|
150
119
|
const curatedDir = resolve2(projectRoot, ".agentpacks", ".curated");
|
|
151
120
|
mkdirSync(curatedDir, { recursive: true });
|
|
152
|
-
|
|
121
|
+
extractNpmPack(parsed, resolvedVersion, curatedDir, installed, warnings);
|
|
153
122
|
const newEntry = {
|
|
154
123
|
requestedRef: parsed.version,
|
|
155
124
|
resolvedRef: resolvedVersion,
|
|
@@ -171,12 +140,17 @@ function extractNpmPack(parsed, version, curatedDir, installed, warnings) {
|
|
|
171
140
|
stdio: "pipe",
|
|
172
141
|
timeout: 30000
|
|
173
142
|
});
|
|
174
|
-
const tgzFiles =
|
|
143
|
+
const tgzFiles = readdirSync(tmpDir).filter((f) => f.endsWith(".tgz"));
|
|
175
144
|
if (tgzFiles.length === 0) {
|
|
176
145
|
warnings.push(`No tarball found for ${pkgSpec}`);
|
|
177
146
|
return packOutDir;
|
|
178
147
|
}
|
|
179
|
-
const
|
|
148
|
+
const firstTgz = tgzFiles[0];
|
|
149
|
+
if (!firstTgz) {
|
|
150
|
+
warnings.push(`No tarball found for ${pkgSpec}`);
|
|
151
|
+
return packOutDir;
|
|
152
|
+
}
|
|
153
|
+
const tgzPath = join(tmpDir, firstTgz);
|
|
180
154
|
mkdirSync(packOutDir, { recursive: true });
|
|
181
155
|
execSync(`tar xzf "${tgzPath}" -C "${packOutDir}" --strip-components=1`, {
|
|
182
156
|
stdio: "pipe",
|
|
@@ -194,8 +168,7 @@ function extractNpmPack(parsed, version, curatedDir, installed, warnings) {
|
|
|
194
168
|
return packOutDir;
|
|
195
169
|
}
|
|
196
170
|
function collectFiles(dir, out) {
|
|
197
|
-
const
|
|
198
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
171
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
199
172
|
for (const entry of entries) {
|
|
200
173
|
const full = join(dir, entry.name);
|
|
201
174
|
if (entry.isDirectory()) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/sources/registry-ref.ts
|
|
5
|
+
function parseRegistrySourceRef(source) {
|
|
6
|
+
let s = source;
|
|
7
|
+
if (s.startsWith("registry:")) {
|
|
8
|
+
s = s.slice(9);
|
|
9
|
+
}
|
|
10
|
+
if (!s) {
|
|
11
|
+
throw new Error(`Invalid registry source reference: "${source}". Expected pack name.`);
|
|
12
|
+
}
|
|
13
|
+
let version = "latest";
|
|
14
|
+
const atIdx = s.indexOf("@");
|
|
15
|
+
if (atIdx > 0) {
|
|
16
|
+
version = s.slice(atIdx + 1);
|
|
17
|
+
s = s.slice(0, atIdx);
|
|
18
|
+
}
|
|
19
|
+
if (!s) {
|
|
20
|
+
throw new Error(`Invalid registry source reference: "${source}". Pack name is empty.`);
|
|
21
|
+
}
|
|
22
|
+
if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(s)) {
|
|
23
|
+
throw new Error(`Invalid registry pack name: "${s}". Must be lowercase alphanumeric with hyphens.`);
|
|
24
|
+
}
|
|
25
|
+
return { packName: s, version };
|
|
26
|
+
}
|
|
27
|
+
function isRegistryPackRef(packRef) {
|
|
28
|
+
return packRef.startsWith("registry:");
|
|
29
|
+
}
|
|
30
|
+
function registrySourceKey(parsed) {
|
|
31
|
+
return `registry:${parsed.packName}`;
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
registrySourceKey,
|
|
35
|
+
parseRegistrySourceRef,
|
|
36
|
+
isRegistryPackRef
|
|
37
|
+
};
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/core/lockfile.ts
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
6
|
+
import { resolve } from "path";
|
|
7
|
+
import { createHash } from "crypto";
|
|
8
|
+
var LOCKFILE_VERSION = 1;
|
|
9
|
+
var LOCKFILE_NAME = "agentpacks.lock";
|
|
10
|
+
function loadLockfile(projectRoot) {
|
|
11
|
+
const filepath = resolve(projectRoot, LOCKFILE_NAME);
|
|
12
|
+
if (!existsSync(filepath)) {
|
|
13
|
+
return { lockfileVersion: LOCKFILE_VERSION, sources: {} };
|
|
14
|
+
}
|
|
15
|
+
const raw = readFileSync(filepath, "utf-8");
|
|
16
|
+
return JSON.parse(raw);
|
|
17
|
+
}
|
|
18
|
+
function saveLockfile(projectRoot, lockfile) {
|
|
19
|
+
const filepath = resolve(projectRoot, LOCKFILE_NAME);
|
|
20
|
+
writeFileSync(filepath, JSON.stringify(lockfile, null, 2) + `
|
|
21
|
+
`);
|
|
22
|
+
}
|
|
23
|
+
function getLockedSource(lockfile, sourceKey) {
|
|
24
|
+
return lockfile.sources[sourceKey];
|
|
25
|
+
}
|
|
26
|
+
function setLockedSource(lockfile, sourceKey, entry) {
|
|
27
|
+
lockfile.sources[sourceKey] = entry;
|
|
28
|
+
}
|
|
29
|
+
function computeIntegrity(content) {
|
|
30
|
+
const hash = createHash("sha256").update(content).digest("hex");
|
|
31
|
+
return `sha256-${hash}`;
|
|
32
|
+
}
|
|
33
|
+
function isLockfileFrozenValid(lockfile, sourceKeys) {
|
|
34
|
+
const missing = sourceKeys.filter((key) => !(key in lockfile.sources));
|
|
35
|
+
return { valid: missing.length === 0, missing };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/sources/registry-ref.ts
|
|
39
|
+
function parseRegistrySourceRef(source) {
|
|
40
|
+
let s = source;
|
|
41
|
+
if (s.startsWith("registry:")) {
|
|
42
|
+
s = s.slice(9);
|
|
43
|
+
}
|
|
44
|
+
if (!s) {
|
|
45
|
+
throw new Error(`Invalid registry source reference: "${source}". Expected pack name.`);
|
|
46
|
+
}
|
|
47
|
+
let version = "latest";
|
|
48
|
+
const atIdx = s.indexOf("@");
|
|
49
|
+
if (atIdx > 0) {
|
|
50
|
+
version = s.slice(atIdx + 1);
|
|
51
|
+
s = s.slice(0, atIdx);
|
|
52
|
+
}
|
|
53
|
+
if (!s) {
|
|
54
|
+
throw new Error(`Invalid registry source reference: "${source}". Pack name is empty.`);
|
|
55
|
+
}
|
|
56
|
+
if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(s)) {
|
|
57
|
+
throw new Error(`Invalid registry pack name: "${s}". Must be lowercase alphanumeric with hyphens.`);
|
|
58
|
+
}
|
|
59
|
+
return { packName: s, version };
|
|
60
|
+
}
|
|
61
|
+
function isRegistryPackRef(packRef) {
|
|
62
|
+
return packRef.startsWith("registry:");
|
|
63
|
+
}
|
|
64
|
+
function registrySourceKey(parsed) {
|
|
65
|
+
return `registry:${parsed.packName}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/utils/registry-client.ts
|
|
69
|
+
var DEFAULT_REGISTRY_URL = "https://registry.agentpacks.dev";
|
|
70
|
+
function createRegistryClient(config) {
|
|
71
|
+
return new RegistryClient({
|
|
72
|
+
registryUrl: config?.registryUrl ?? DEFAULT_REGISTRY_URL,
|
|
73
|
+
authToken: config?.authToken,
|
|
74
|
+
timeout: config?.timeout ?? 30000
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
class RegistryClient {
|
|
79
|
+
config;
|
|
80
|
+
constructor(config) {
|
|
81
|
+
this.config = {
|
|
82
|
+
registryUrl: config.registryUrl.replace(/\/+$/, ""),
|
|
83
|
+
authToken: config.authToken,
|
|
84
|
+
timeout: config.timeout ?? 30000
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
async search(params) {
|
|
88
|
+
const searchParams = new URLSearchParams;
|
|
89
|
+
if (params.query)
|
|
90
|
+
searchParams.set("q", params.query);
|
|
91
|
+
if (params.tags?.length)
|
|
92
|
+
searchParams.set("tags", params.tags.join(","));
|
|
93
|
+
if (params.targets?.length)
|
|
94
|
+
searchParams.set("targets", params.targets.join(","));
|
|
95
|
+
if (params.features?.length)
|
|
96
|
+
searchParams.set("features", params.features.join(","));
|
|
97
|
+
if (params.author)
|
|
98
|
+
searchParams.set("author", params.author);
|
|
99
|
+
if (params.sort)
|
|
100
|
+
searchParams.set("sort", params.sort);
|
|
101
|
+
if (params.limit)
|
|
102
|
+
searchParams.set("limit", String(params.limit));
|
|
103
|
+
if (params.offset)
|
|
104
|
+
searchParams.set("offset", String(params.offset));
|
|
105
|
+
const url = `${this.config.registryUrl}/packs?${searchParams.toString()}`;
|
|
106
|
+
const res = await this.fetch(url);
|
|
107
|
+
return res;
|
|
108
|
+
}
|
|
109
|
+
async info(packName) {
|
|
110
|
+
const url = `${this.config.registryUrl}/packs/${encodeURIComponent(packName)}`;
|
|
111
|
+
const res = await this.fetch(url);
|
|
112
|
+
return res;
|
|
113
|
+
}
|
|
114
|
+
async download(packName, version = "latest") {
|
|
115
|
+
const url = `${this.config.registryUrl}/packs/${encodeURIComponent(packName)}/versions/${encodeURIComponent(version)}/download`;
|
|
116
|
+
const response = await fetch(url, {
|
|
117
|
+
headers: this.headers(),
|
|
118
|
+
signal: AbortSignal.timeout(this.config.timeout)
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new RegistryApiError(response.status, `Failed to download ${packName}@${version}: ${response.statusText}`);
|
|
122
|
+
}
|
|
123
|
+
const integrity = response.headers.get("x-integrity") ?? "";
|
|
124
|
+
const data = await response.arrayBuffer();
|
|
125
|
+
return { data, integrity };
|
|
126
|
+
}
|
|
127
|
+
async publish(tarball, metadata) {
|
|
128
|
+
if (!this.config.authToken) {
|
|
129
|
+
throw new Error("Authentication required. Run `agentpacks login` first.");
|
|
130
|
+
}
|
|
131
|
+
const formData = new FormData;
|
|
132
|
+
formData.append("tarball", new Blob([tarball], { type: "application/gzip" }), `${metadata.name}-${metadata.version}.tgz`);
|
|
133
|
+
formData.append("metadata", JSON.stringify(metadata));
|
|
134
|
+
const url = `${this.config.registryUrl}/packs`;
|
|
135
|
+
const response = await fetch(url, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: {
|
|
138
|
+
Authorization: `Bearer ${this.config.authToken}`
|
|
139
|
+
},
|
|
140
|
+
body: formData,
|
|
141
|
+
signal: AbortSignal.timeout(this.config.timeout)
|
|
142
|
+
});
|
|
143
|
+
if (!response.ok) {
|
|
144
|
+
const body = await response.text();
|
|
145
|
+
throw new RegistryApiError(response.status, `Publish failed: ${body || response.statusText}`);
|
|
146
|
+
}
|
|
147
|
+
return await response.json();
|
|
148
|
+
}
|
|
149
|
+
async featured(limit) {
|
|
150
|
+
const url = limit ? `${this.config.registryUrl}/featured?limit=${limit}` : `${this.config.registryUrl}/featured`;
|
|
151
|
+
const res = await this.fetch(url);
|
|
152
|
+
return res.packs;
|
|
153
|
+
}
|
|
154
|
+
async tags() {
|
|
155
|
+
const url = `${this.config.registryUrl}/tags`;
|
|
156
|
+
const res = await this.fetch(url);
|
|
157
|
+
return res.tags;
|
|
158
|
+
}
|
|
159
|
+
async stats() {
|
|
160
|
+
const url = `${this.config.registryUrl}/stats`;
|
|
161
|
+
return await this.fetch(url);
|
|
162
|
+
}
|
|
163
|
+
async health() {
|
|
164
|
+
try {
|
|
165
|
+
const url = `${this.config.registryUrl}/health`;
|
|
166
|
+
await this.fetch(url);
|
|
167
|
+
return true;
|
|
168
|
+
} catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async fetch(url) {
|
|
173
|
+
const response = await fetch(url, {
|
|
174
|
+
headers: this.headers(),
|
|
175
|
+
signal: AbortSignal.timeout(this.config.timeout)
|
|
176
|
+
});
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
const body = await response.text();
|
|
179
|
+
throw new RegistryApiError(response.status, body || response.statusText);
|
|
180
|
+
}
|
|
181
|
+
return response.json();
|
|
182
|
+
}
|
|
183
|
+
headers() {
|
|
184
|
+
const h = {
|
|
185
|
+
Accept: "application/json"
|
|
186
|
+
};
|
|
187
|
+
if (this.config.authToken) {
|
|
188
|
+
h["Authorization"] = `Bearer ${this.config.authToken}`;
|
|
189
|
+
}
|
|
190
|
+
return h;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
class RegistryApiError extends Error {
|
|
195
|
+
status;
|
|
196
|
+
constructor(status, message) {
|
|
197
|
+
super(message);
|
|
198
|
+
this.name = "RegistryApiError";
|
|
199
|
+
this.status = status;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/utils/credentials.ts
|
|
204
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
|
|
205
|
+
import { join, dirname } from "path";
|
|
206
|
+
import { homedir } from "os";
|
|
207
|
+
var CONFIG_DIR = join(homedir(), ".config", "agentpacks");
|
|
208
|
+
var CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
|
|
209
|
+
function loadCredentials() {
|
|
210
|
+
if (!existsSync2(CREDENTIALS_FILE)) {
|
|
211
|
+
return { registryUrl: "https://registry.agentpacks.dev" };
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const raw = readFileSync2(CREDENTIALS_FILE, "utf-8");
|
|
215
|
+
return JSON.parse(raw);
|
|
216
|
+
} catch {
|
|
217
|
+
return { registryUrl: "https://registry.agentpacks.dev" };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function saveCredentials(credentials) {
|
|
221
|
+
mkdirSync(dirname(CREDENTIALS_FILE), { recursive: true });
|
|
222
|
+
writeFileSync2(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2) + `
|
|
223
|
+
`, {
|
|
224
|
+
mode: 384
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
function clearCredentials() {
|
|
228
|
+
if (existsSync2(CREDENTIALS_FILE)) {
|
|
229
|
+
writeFileSync2(CREDENTIALS_FILE, JSON.stringify({ registryUrl: "https://registry.agentpacks.dev" }) + `
|
|
230
|
+
`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/utils/tarball.ts
|
|
235
|
+
import { execSync } from "child_process";
|
|
236
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2, rmSync } from "fs";
|
|
237
|
+
import { join as join2, resolve as resolve2 } from "path";
|
|
238
|
+
import { createHash as createHash2 } from "crypto";
|
|
239
|
+
import { tmpdir } from "os";
|
|
240
|
+
async function createTarball(packDir) {
|
|
241
|
+
const absDir = resolve2(packDir);
|
|
242
|
+
const tmpFile = join2(tmpdir(), `agentpacks-${Date.now()}.tgz`);
|
|
243
|
+
try {
|
|
244
|
+
execSync(`tar -czf "${tmpFile}" -C "${absDir}" .`, {
|
|
245
|
+
stdio: "pipe"
|
|
246
|
+
});
|
|
247
|
+
const buffer = readFileSync3(tmpFile);
|
|
248
|
+
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
249
|
+
} finally {
|
|
250
|
+
if (existsSync3(tmpFile)) {
|
|
251
|
+
rmSync(tmpFile);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function extractTarball(data, targetDir) {
|
|
256
|
+
mkdirSync2(targetDir, { recursive: true });
|
|
257
|
+
const tmpFile = join2(tmpdir(), `agentpacks-${Date.now()}.tgz`);
|
|
258
|
+
try {
|
|
259
|
+
const buffer = Buffer.from(data);
|
|
260
|
+
const { writeFileSync: writeFileSync3 } = await import("fs");
|
|
261
|
+
writeFileSync3(tmpFile, buffer);
|
|
262
|
+
execSync(`tar -xzf "${tmpFile}" -C "${targetDir}"`, {
|
|
263
|
+
stdio: "pipe"
|
|
264
|
+
});
|
|
265
|
+
} finally {
|
|
266
|
+
if (existsSync3(tmpFile)) {
|
|
267
|
+
rmSync(tmpFile);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function computeTarballIntegrity(data) {
|
|
272
|
+
const hash = createHash2("sha256").update(Buffer.from(data)).digest("hex");
|
|
273
|
+
return `sha256-${hash}`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// src/sources/registry.ts
|
|
277
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, rmSync as rmSync2 } from "fs";
|
|
278
|
+
import { resolve as resolve3, join as join3 } from "path";
|
|
279
|
+
async function installRegistrySource(projectRoot, source, lockfile, options = {}) {
|
|
280
|
+
const parsed = parseRegistrySourceRef(source);
|
|
281
|
+
const sourceKey = registrySourceKey(parsed);
|
|
282
|
+
const installed = [];
|
|
283
|
+
const warnings = [];
|
|
284
|
+
const locked = getLockedSource(lockfile, sourceKey);
|
|
285
|
+
if (options.frozen && !locked) {
|
|
286
|
+
throw new Error(`Frozen mode: no lockfile entry for source "${sourceKey}".`);
|
|
287
|
+
}
|
|
288
|
+
let targetVersion = parsed.version;
|
|
289
|
+
if (locked && !options.update) {
|
|
290
|
+
targetVersion = locked.resolvedRef;
|
|
291
|
+
} else if (targetVersion === "latest") {
|
|
292
|
+
const clientCfg2 = buildClientConfig(options.registryUrl);
|
|
293
|
+
const client2 = createRegistryClient(clientCfg2);
|
|
294
|
+
const info = await client2.info(parsed.packName);
|
|
295
|
+
targetVersion = info.latestVersion;
|
|
296
|
+
}
|
|
297
|
+
const clientCfg = buildClientConfig(options.registryUrl);
|
|
298
|
+
const client = createRegistryClient(clientCfg);
|
|
299
|
+
const { data } = await client.download(parsed.packName, targetVersion);
|
|
300
|
+
const localIntegrity = computeTarballIntegrity(data);
|
|
301
|
+
if (locked && !options.update && locked.skills?.["__integrity"]) {
|
|
302
|
+
const expectedIntegrity = locked.skills["__integrity"].integrity;
|
|
303
|
+
if (expectedIntegrity && localIntegrity !== expectedIntegrity) {
|
|
304
|
+
throw new Error(`Integrity mismatch for ${parsed.packName}@${targetVersion}. ` + `Expected ${expectedIntegrity}, got ${localIntegrity}.`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const curatedDir = resolve3(projectRoot, ".agentpacks", ".curated");
|
|
308
|
+
const packOutDir = resolve3(curatedDir, parsed.packName);
|
|
309
|
+
if (existsSync4(packOutDir)) {
|
|
310
|
+
rmSync2(packOutDir, { recursive: true, force: true });
|
|
311
|
+
}
|
|
312
|
+
mkdirSync3(packOutDir, { recursive: true });
|
|
313
|
+
await extractTarball(data, packOutDir);
|
|
314
|
+
collectFiles(packOutDir, installed);
|
|
315
|
+
const newEntry = {
|
|
316
|
+
requestedRef: parsed.version,
|
|
317
|
+
resolvedRef: targetVersion,
|
|
318
|
+
resolvedAt: new Date().toISOString(),
|
|
319
|
+
skills: {
|
|
320
|
+
__integrity: { integrity: localIntegrity }
|
|
321
|
+
},
|
|
322
|
+
packs: {
|
|
323
|
+
[parsed.packName]: { integrity: localIntegrity }
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
setLockedSource(lockfile, sourceKey, newEntry);
|
|
327
|
+
return { installed, warnings };
|
|
328
|
+
}
|
|
329
|
+
function buildClientConfig(registryUrl) {
|
|
330
|
+
const cfg = {};
|
|
331
|
+
if (registryUrl) {
|
|
332
|
+
cfg.registryUrl = registryUrl;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
const creds = loadCredentials();
|
|
336
|
+
if (creds?.token) {
|
|
337
|
+
cfg.authToken = creds.token;
|
|
338
|
+
}
|
|
339
|
+
} catch {}
|
|
340
|
+
return cfg;
|
|
341
|
+
}
|
|
342
|
+
function collectFiles(dir, out) {
|
|
343
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
344
|
+
for (const entry of entries) {
|
|
345
|
+
const full = join3(dir, entry.name);
|
|
346
|
+
if (entry.isDirectory()) {
|
|
347
|
+
collectFiles(full, out);
|
|
348
|
+
} else {
|
|
349
|
+
out.push(full);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
export {
|
|
354
|
+
installRegistrySource
|
|
355
|
+
};
|