@kb-labs/marketplace-npm 2.94.0 → 2.98.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/dist/index.d.ts CHANGED
@@ -19,4 +19,48 @@ declare class NpmPackageSource implements PackageSource {
19
19
  remove(packageId: string, root: string): Promise<void>;
20
20
  }
21
21
 
22
- export { NpmPackageSource, PackageInstallError };
22
+ /**
23
+ * @module @kb-labs/marketplace-npm/registry-source
24
+ * KB Labs Registry PackageSource implementation.
25
+ * Resolves/installs packages from the KB Registry (kb:handle/name spec).
26
+ */
27
+
28
+ declare class RegistryPackageError extends Error {
29
+ readonly code: string;
30
+ constructor(code: string, message: string);
31
+ }
32
+ interface RegistrySourceOptions {
33
+ /** Base URL of the registry (e.g. https://registry.kblabs.ru) */
34
+ registryUrl: string;
35
+ /** Bearer token for authenticated (private) package access */
36
+ authToken?: string;
37
+ /** Share token for token-based private access */
38
+ shareToken?: string;
39
+ /** Ed25519 public key (PEM) for signature verification */
40
+ signingPublicKey?: string;
41
+ }
42
+ declare class RegistryPackageSource implements PackageSource {
43
+ private readonly baseUrl;
44
+ private readonly authToken?;
45
+ private readonly shareToken?;
46
+ private readonly signingPublicKey?;
47
+ constructor(opts: RegistrySourceOptions);
48
+ /** Returns true if spec is a kb: registry spec */
49
+ static accepts(spec: string): boolean;
50
+ /** Parse 'kb:handle/name[@version]' → { handle, name, version }
51
+ * Handles scoped package names like kb:handle/@scope/name[@version].
52
+ */
53
+ static parseSpec(spec: string): {
54
+ handle: string;
55
+ name: string;
56
+ version: string;
57
+ };
58
+ resolve(spec: string): Promise<ResolvedPackage>;
59
+ install(pkg: ResolvedPackage, root: string): Promise<InstalledPackage>;
60
+ withShareToken(token: string): RegistryPackageSource;
61
+ remove(_packageId: string, _root: string): Promise<void>;
62
+ private fetch;
63
+ private verifySignature;
64
+ }
65
+
66
+ export { NpmPackageSource, PackageInstallError, RegistryPackageError, RegistryPackageSource, type RegistrySourceOptions };
package/dist/index.js CHANGED
@@ -1,7 +1,10 @@
1
1
  import { execa } from 'execa';
2
2
  import * as path from 'path';
3
- import * as fs from 'fs/promises';
3
+ import * as fs2 from 'fs/promises';
4
4
  import * as crypto from 'crypto';
5
+ import { createHash, createVerify } from 'crypto';
6
+ import { extract } from 'tar';
7
+ import * as os from 'os';
5
8
 
6
9
  // src/npm-source.ts
7
10
  var PackageInstallError = class extends Error {
@@ -45,7 +48,7 @@ var NpmPackageSource = class {
45
48
  const packageRoot = path.join(root, "node_modules", pkg.id);
46
49
  let version = pkg.version;
47
50
  try {
48
- const pkgJson = JSON.parse(await fs.readFile(path.join(packageRoot, "package.json"), "utf-8"));
51
+ const pkgJson = JSON.parse(await fs2.readFile(path.join(packageRoot, "package.json"), "utf-8"));
49
52
  version = pkgJson.version ?? version;
50
53
  } catch {
51
54
  }
@@ -87,14 +90,155 @@ function extractVersion(spec) {
87
90
  }
88
91
  async function computeIntegrity(packageRoot) {
89
92
  try {
90
- const content = await fs.readFile(path.join(packageRoot, "package.json"));
93
+ const content = await fs2.readFile(path.join(packageRoot, "package.json"));
91
94
  const hash = crypto.createHash("sha256").update(content).digest("base64");
92
95
  return `sha256-${hash}`;
93
96
  } catch {
94
97
  return "";
95
98
  }
96
99
  }
100
+ var RegistryPackageError = class extends Error {
101
+ code;
102
+ constructor(code, message) {
103
+ super(message);
104
+ this.name = "RegistryPackageError";
105
+ this.code = code;
106
+ }
107
+ };
108
+ var RegistryPackageSource = class _RegistryPackageSource {
109
+ baseUrl;
110
+ authToken;
111
+ shareToken;
112
+ signingPublicKey;
113
+ constructor(opts) {
114
+ this.baseUrl = opts.registryUrl.replace(/\/$/, "");
115
+ this.authToken = opts.authToken;
116
+ this.shareToken = opts.shareToken;
117
+ this.signingPublicKey = opts.signingPublicKey;
118
+ }
119
+ /** Returns true if spec is a kb: registry spec */
120
+ static accepts(spec) {
121
+ return spec.startsWith("kb:");
122
+ }
123
+ /** Parse 'kb:handle/name[@version]' → { handle, name, version }
124
+ * Handles scoped package names like kb:handle/@scope/name[@version].
125
+ */
126
+ static parseSpec(spec) {
127
+ const raw = spec.slice(3);
128
+ const firstSlash = raw.indexOf("/");
129
+ if (firstSlash === -1) {
130
+ throw new RegistryPackageError("INVALID_SPEC", `Invalid kb: spec '${spec}'. Expected kb:handle/name[@version]`);
131
+ }
132
+ const handle = raw.slice(0, firstSlash);
133
+ const nameAndVersion = raw.slice(firstSlash + 1);
134
+ let versionAtIdx = -1;
135
+ if (nameAndVersion.startsWith("@")) {
136
+ const secondSlash = nameAndVersion.indexOf("/", 1);
137
+ if (secondSlash !== -1) {
138
+ versionAtIdx = nameAndVersion.indexOf("@", secondSlash + 1);
139
+ }
140
+ } else {
141
+ versionAtIdx = nameAndVersion.indexOf("@");
142
+ }
143
+ const name = versionAtIdx > 0 ? nameAndVersion.slice(0, versionAtIdx) : nameAndVersion;
144
+ const version = versionAtIdx > 0 ? nameAndVersion.slice(versionAtIdx + 1) : "latest";
145
+ if (!handle || !name) {
146
+ throw new RegistryPackageError("INVALID_SPEC", `Invalid kb: spec '${spec}'. Expected kb:handle/name[@version]`);
147
+ }
148
+ return { handle, name, version };
149
+ }
150
+ async resolve(spec) {
151
+ const { handle, name, version } = _RegistryPackageSource.parseSpec(spec);
152
+ const url = `${this.baseUrl}/api/v1/packages/${handle}/${encodeURIComponent(name)}`;
153
+ const resp = await this.fetch(url);
154
+ if (!resp.ok) {
155
+ throw new RegistryPackageError("PACKAGE_NOT_FOUND", `Package ${handle}/${name} not found in registry`);
156
+ }
157
+ const entry = await resp.json();
158
+ const versions = entry.versions ?? [];
159
+ const resolved = version === "latest" ? versions.filter((v) => !v.yanked).at(-1) : versions.find((v) => v.version === version);
160
+ if (!resolved) {
161
+ throw new RegistryPackageError("VERSION_NOT_FOUND", `Version ${version} not found for ${handle}/${name}`);
162
+ }
163
+ if (resolved.yanked) {
164
+ throw new RegistryPackageError("VERSION_YANKED", `Version ${version} of ${handle}/${name} has been yanked`);
165
+ }
166
+ return {
167
+ id: `${handle}/${name}`,
168
+ version: resolved.version,
169
+ integrity: resolved.integrity,
170
+ signature: resolved.signature,
171
+ source: "marketplace",
172
+ downloadUrl: `${this.baseUrl}/api/v1/packages/${handle}/${encodeURIComponent(name)}/${resolved.version}/tarball`
173
+ };
174
+ }
175
+ async install(pkg, root) {
176
+ if (!pkg.downloadUrl) {
177
+ throw new RegistryPackageError("NO_DOWNLOAD_URL", "Missing downloadUrl in ResolvedPackage");
178
+ }
179
+ const resp = await this.fetch(pkg.downloadUrl);
180
+ if (!resp.ok) {
181
+ throw new RegistryPackageError("DOWNLOAD_FAILED", `Failed to download ${pkg.id}@${pkg.version}: ${resp.status}`);
182
+ }
183
+ const tarball = Buffer.from(await resp.arrayBuffer());
184
+ const actualIntegrity = `sha256-${createHash("sha256").update(tarball).digest("base64")}`;
185
+ if (pkg.integrity && actualIntegrity !== pkg.integrity) {
186
+ throw new RegistryPackageError("INTEGRITY_MISMATCH", `Integrity mismatch for ${pkg.id}@${pkg.version}`);
187
+ }
188
+ if (pkg.signature) {
189
+ this.verifySignature(tarball, pkg.integrity, pkg.signature, pkg.id, pkg.version);
190
+ }
191
+ const packageRoot = path.join(root, "node_modules", pkg.id);
192
+ await fs2.mkdir(packageRoot, { recursive: true });
193
+ const tmpFile = path.join(os.tmpdir(), `kb-registry-${Date.now()}.tgz`);
194
+ try {
195
+ await fs2.writeFile(tmpFile, tarball);
196
+ await extract({ file: tmpFile, cwd: packageRoot, strip: 1 });
197
+ } finally {
198
+ await fs2.unlink(tmpFile).catch(() => {
199
+ });
200
+ }
201
+ return { id: pkg.id, version: pkg.version, packageRoot, integrity: actualIntegrity };
202
+ }
203
+ withShareToken(token) {
204
+ return new _RegistryPackageSource({
205
+ registryUrl: this.baseUrl,
206
+ authToken: this.authToken,
207
+ shareToken: token,
208
+ signingPublicKey: this.signingPublicKey
209
+ });
210
+ }
211
+ async remove(_packageId, _root) {
212
+ throw new RegistryPackageError("UNSUPPORTED", "Use npm source for remove operations");
213
+ }
214
+ // ── Private helpers ────────────────────────────────────────────────────────
215
+ async fetch(url) {
216
+ const headers = {};
217
+ if (this.authToken) {
218
+ headers["Authorization"] = `Bearer ${this.authToken}`;
219
+ }
220
+ if (this.shareToken) {
221
+ headers["X-Share-Token"] = this.shareToken;
222
+ }
223
+ return globalThis.fetch(url, { headers });
224
+ }
225
+ verifySignature(tarball, integrity, signature, id, version) {
226
+ if (!this.signingPublicKey) {
227
+ return;
228
+ }
229
+ const payload = Buffer.from(JSON.stringify({ integrity, pkg: `${id}@${version}` }));
230
+ const verify = createVerify("sha256");
231
+ verify.update(payload);
232
+ const valid = verify.verify(
233
+ { key: this.signingPublicKey, format: "pem" },
234
+ Buffer.from(signature.value, "base64")
235
+ );
236
+ if (!valid) {
237
+ throw new RegistryPackageError("SIGNATURE_INVALID", `Platform signature invalid for ${id}@${version}`);
238
+ }
239
+ }
240
+ };
97
241
 
98
- export { NpmPackageSource, PackageInstallError };
242
+ export { NpmPackageSource, PackageInstallError, RegistryPackageError, RegistryPackageSource };
99
243
  //# sourceMappingURL=index.js.map
100
244
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/npm-source.ts"],"names":["versionSep"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/marketplace-npm/npm-source\n * pnpm-based PackageSource implementation.\n * This is the ONLY place in the marketplace codebase that calls pnpm.\n */\n\nimport { execa } from 'execa';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport * as crypto from 'node:crypto';\nimport type {\n PackageSource,\n ResolvedPackage,\n InstalledPackage,\n} from '@kb-labs/marketplace-contracts';\n\nexport class PackageInstallError extends Error {\n readonly code: 'NOT_FOUND' | 'INSTALL_FAILED';\n readonly spec: string;\n constructor(code: PackageInstallError['code'], spec: string, message: string) {\n super(message);\n this.name = 'PackageInstallError';\n this.code = code;\n this.spec = spec;\n }\n}\n\nexport class NpmPackageSource implements PackageSource {\n async resolve(spec: string): Promise<ResolvedPackage> {\n const id = extractPackageName(spec);\n if (!id) {\n throw new Error(`Invalid package spec: ${spec}`);\n }\n\n return {\n id,\n version: extractVersion(spec) ?? 'latest',\n integrity: '', // computed after install\n source: 'marketplace',\n };\n }\n\n async install(\n pkg: ResolvedPackage,\n root: string,\n opts?: { dev?: boolean },\n ): Promise<InstalledPackage> {\n const spec = pkg.version === 'latest' ? pkg.id : `${pkg.id}@${pkg.version}`;\n const args = ['add', spec];\n if (opts?.dev) {args.push('--save-dev');}\n // Bypass the pnpm workspace-root guard: kb-create projects are flat (single-package),\n // so adding to the project root is intentional. Without this flag pnpm throws\n // ERR_PNPM_ADDING_TO_ROOT when a pnpm-workspace.yaml exists in the project directory.\n args.push('--ignore-workspace-root-check');\n\n try {\n await execa('pnpm', args, { cwd: root, timeout: 5 * 60 * 1000 });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const code = msg.includes('ERR_PNPM_FETCH_404') || msg.includes('Not Found - 404')\n ? 'NOT_FOUND'\n : 'INSTALL_FAILED';\n throw new PackageInstallError(code, spec, `pnpm add ${spec} failed: ${msg}`);\n }\n\n const packageRoot = path.join(root, 'node_modules', pkg.id);\n\n // Read actual installed version\n let version = pkg.version;\n try {\n const pkgJson = JSON.parse(await fs.readFile(path.join(packageRoot, 'package.json'), 'utf-8'));\n version = pkgJson.version ?? version;\n } catch { /* use spec version */ }\n\n // Compute integrity\n const integrity = await computeIntegrity(packageRoot);\n\n return {\n id: pkg.id,\n version,\n packageRoot,\n integrity,\n };\n }\n\n async remove(packageId: string, root: string): Promise<void> {\n await execa('pnpm', ['remove', packageId], { cwd: root, timeout: 2 * 60 * 1000 });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction extractPackageName(spec: string): string | null {\n if (!spec || spec.startsWith('.') || spec.startsWith('/')) {return null;}\n\n if (spec.startsWith('@')) {\n const slashIndex = spec.indexOf('/');\n if (slashIndex === -1) {return null;}\n const versionSep = spec.lastIndexOf('@');\n return versionSep > slashIndex ? spec.slice(0, versionSep) : spec;\n }\n\n const versionSep = spec.indexOf('@');\n return versionSep > 0 ? spec.slice(0, versionSep) : spec;\n}\n\nfunction extractVersion(spec: string): string | null {\n if (spec.startsWith('@')) {\n const slashIndex = spec.indexOf('/');\n const lastAt = spec.lastIndexOf('@');\n return lastAt > slashIndex ? spec.slice(lastAt + 1) : null;\n }\n const atIndex = spec.indexOf('@');\n return atIndex > 0 ? spec.slice(atIndex + 1) : null;\n}\n\nasync function computeIntegrity(packageRoot: string): Promise<string> {\n try {\n const content = await fs.readFile(path.join(packageRoot, 'package.json'));\n const hash = crypto.createHash('sha256').update(content).digest('base64');\n return `sha256-${hash}`;\n } catch {\n return '';\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/npm-source.ts","../src/registry-source.ts"],"names":["fs","versionSep","createHash","path2"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/marketplace-npm/npm-source\n * pnpm-based PackageSource implementation.\n * This is the ONLY place in the marketplace codebase that calls pnpm.\n */\n\nimport { execa } from 'execa';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport * as crypto from 'node:crypto';\nimport type {\n PackageSource,\n ResolvedPackage,\n InstalledPackage,\n} from '@kb-labs/marketplace-contracts';\n\nexport class PackageInstallError extends Error {\n readonly code: 'NOT_FOUND' | 'INSTALL_FAILED';\n readonly spec: string;\n constructor(code: PackageInstallError['code'], spec: string, message: string) {\n super(message);\n this.name = 'PackageInstallError';\n this.code = code;\n this.spec = spec;\n }\n}\n\nexport class NpmPackageSource implements PackageSource {\n async resolve(spec: string): Promise<ResolvedPackage> {\n const id = extractPackageName(spec);\n if (!id) {\n throw new Error(`Invalid package spec: ${spec}`);\n }\n\n return {\n id,\n version: extractVersion(spec) ?? 'latest',\n integrity: '', // computed after install\n source: 'marketplace',\n };\n }\n\n async install(\n pkg: ResolvedPackage,\n root: string,\n opts?: { dev?: boolean },\n ): Promise<InstalledPackage> {\n const spec = pkg.version === 'latest' ? pkg.id : `${pkg.id}@${pkg.version}`;\n const args = ['add', spec];\n if (opts?.dev) {args.push('--save-dev');}\n // Bypass the pnpm workspace-root guard: kb-create projects are flat (single-package),\n // so adding to the project root is intentional. Without this flag pnpm throws\n // ERR_PNPM_ADDING_TO_ROOT when a pnpm-workspace.yaml exists in the project directory.\n args.push('--ignore-workspace-root-check');\n\n try {\n await execa('pnpm', args, { cwd: root, timeout: 5 * 60 * 1000 });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const code = msg.includes('ERR_PNPM_FETCH_404') || msg.includes('Not Found - 404')\n ? 'NOT_FOUND'\n : 'INSTALL_FAILED';\n throw new PackageInstallError(code, spec, `pnpm add ${spec} failed: ${msg}`);\n }\n\n const packageRoot = path.join(root, 'node_modules', pkg.id);\n\n // Read actual installed version\n let version = pkg.version;\n try {\n const pkgJson = JSON.parse(await fs.readFile(path.join(packageRoot, 'package.json'), 'utf-8'));\n version = pkgJson.version ?? version;\n } catch { /* use spec version */ }\n\n // Compute integrity\n const integrity = await computeIntegrity(packageRoot);\n\n return {\n id: pkg.id,\n version,\n packageRoot,\n integrity,\n };\n }\n\n async remove(packageId: string, root: string): Promise<void> {\n await execa('pnpm', ['remove', packageId], { cwd: root, timeout: 2 * 60 * 1000 });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction extractPackageName(spec: string): string | null {\n if (!spec || spec.startsWith('.') || spec.startsWith('/')) {return null;}\n\n if (spec.startsWith('@')) {\n const slashIndex = spec.indexOf('/');\n if (slashIndex === -1) {return null;}\n const versionSep = spec.lastIndexOf('@');\n return versionSep > slashIndex ? spec.slice(0, versionSep) : spec;\n }\n\n const versionSep = spec.indexOf('@');\n return versionSep > 0 ? spec.slice(0, versionSep) : spec;\n}\n\nfunction extractVersion(spec: string): string | null {\n if (spec.startsWith('@')) {\n const slashIndex = spec.indexOf('/');\n const lastAt = spec.lastIndexOf('@');\n return lastAt > slashIndex ? spec.slice(lastAt + 1) : null;\n }\n const atIndex = spec.indexOf('@');\n return atIndex > 0 ? spec.slice(atIndex + 1) : null;\n}\n\nasync function computeIntegrity(packageRoot: string): Promise<string> {\n try {\n const content = await fs.readFile(path.join(packageRoot, 'package.json'));\n const hash = crypto.createHash('sha256').update(content).digest('base64');\n return `sha256-${hash}`;\n } catch {\n return '';\n }\n}\n","/**\n * @module @kb-labs/marketplace-npm/registry-source\n * KB Labs Registry PackageSource implementation.\n * Resolves/installs packages from the KB Registry (kb:handle/name spec).\n */\n\nimport { createHash, createVerify } from 'node:crypto';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport { extract } from 'tar';\nimport * as os from 'node:os';\nimport type { PackageSource, ResolvedPackage, InstalledPackage } from '@kb-labs/marketplace-contracts';\nimport type { EntitySignature } from '@kb-labs/core-discovery';\n\nexport class RegistryPackageError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.name = 'RegistryPackageError';\n this.code = code;\n }\n}\n\nexport interface RegistrySourceOptions {\n /** Base URL of the registry (e.g. https://registry.kblabs.ru) */\n registryUrl: string;\n /** Bearer token for authenticated (private) package access */\n authToken?: string;\n /** Share token for token-based private access */\n shareToken?: string;\n /** Ed25519 public key (PEM) for signature verification */\n signingPublicKey?: string;\n}\n\nexport class RegistryPackageSource implements PackageSource {\n private readonly baseUrl: string;\n private readonly authToken?: string;\n private readonly shareToken?: string;\n private readonly signingPublicKey?: string;\n\n constructor(opts: RegistrySourceOptions) {\n this.baseUrl = opts.registryUrl.replace(/\\/$/, '');\n this.authToken = opts.authToken;\n this.shareToken = opts.shareToken;\n this.signingPublicKey = opts.signingPublicKey;\n }\n\n /** Returns true if spec is a kb: registry spec */\n static accepts(spec: string): boolean {\n return spec.startsWith('kb:');\n }\n\n /** Parse 'kb:handle/name[@version]' → { handle, name, version }\n * Handles scoped package names like kb:handle/@scope/name[@version].\n */\n static parseSpec(spec: string): { handle: string; name: string; version: string } {\n const raw = spec.slice(3); // strip 'kb:'\n const firstSlash = raw.indexOf('/');\n if (firstSlash === -1) {\n throw new RegistryPackageError('INVALID_SPEC', `Invalid kb: spec '${spec}'. Expected kb:handle/name[@version]`);\n }\n const handle = raw.slice(0, firstSlash);\n const nameAndVersion = raw.slice(firstSlash + 1);\n\n // Find version separator: last '@' that appears after the package name.\n // For scoped names (@scope/pkg), skip the leading '@' and the slash that follows it.\n let versionAtIdx = -1;\n if (nameAndVersion.startsWith('@')) {\n // scoped: @scope/pkg[@version] — look for '@' after the second '/'\n const secondSlash = nameAndVersion.indexOf('/', 1);\n if (secondSlash !== -1) {\n versionAtIdx = nameAndVersion.indexOf('@', secondSlash + 1);\n }\n } else {\n versionAtIdx = nameAndVersion.indexOf('@');\n }\n\n const name = versionAtIdx > 0 ? nameAndVersion.slice(0, versionAtIdx) : nameAndVersion;\n const version = versionAtIdx > 0 ? nameAndVersion.slice(versionAtIdx + 1) : 'latest';\n\n if (!handle || !name) {\n throw new RegistryPackageError('INVALID_SPEC', `Invalid kb: spec '${spec}'. Expected kb:handle/name[@version]`);\n }\n return { handle, name, version };\n }\n\n async resolve(spec: string): Promise<ResolvedPackage> {\n const { handle, name, version } = RegistryPackageSource.parseSpec(spec);\n const url = `${this.baseUrl}/api/v1/packages/${handle}/${encodeURIComponent(name)}`;\n const resp = await this.fetch(url);\n if (!resp.ok) {\n throw new RegistryPackageError('PACKAGE_NOT_FOUND', `Package ${handle}/${name} not found in registry`);\n }\n const entry = await resp.json() as { versions?: Array<{ version: string; integrity: string; yanked?: boolean; signature?: EntitySignature }> };\n\n const versions = entry.versions ?? [];\n const resolved = version === 'latest'\n ? versions.filter(v => !v.yanked).at(-1)\n : versions.find(v => v.version === version);\n\n if (!resolved) {\n throw new RegistryPackageError('VERSION_NOT_FOUND', `Version ${version} not found for ${handle}/${name}`);\n }\n if (resolved.yanked) {\n throw new RegistryPackageError('VERSION_YANKED', `Version ${version} of ${handle}/${name} has been yanked`);\n }\n\n return {\n id: `${handle}/${name}`,\n version: resolved.version,\n integrity: resolved.integrity,\n signature: resolved.signature,\n source: 'marketplace',\n downloadUrl: `${this.baseUrl}/api/v1/packages/${handle}/${encodeURIComponent(name)}/${resolved.version}/tarball`,\n };\n }\n\n async install(pkg: ResolvedPackage, root: string): Promise<InstalledPackage> {\n if (!pkg.downloadUrl) {\n throw new RegistryPackageError('NO_DOWNLOAD_URL', 'Missing downloadUrl in ResolvedPackage');\n }\n\n const resp = await this.fetch(pkg.downloadUrl);\n if (!resp.ok) {\n throw new RegistryPackageError('DOWNLOAD_FAILED', `Failed to download ${pkg.id}@${pkg.version}: ${resp.status}`);\n }\n\n const tarball = Buffer.from(await resp.arrayBuffer());\n\n // Verify integrity\n const actualIntegrity = `sha256-${createHash('sha256').update(tarball).digest('base64')}`;\n if (pkg.integrity && actualIntegrity !== pkg.integrity) {\n throw new RegistryPackageError('INTEGRITY_MISMATCH', `Integrity mismatch for ${pkg.id}@${pkg.version}`);\n }\n\n // Verify platform signature (for trusted packages)\n if (pkg.signature) {\n this.verifySignature(tarball, pkg.integrity, pkg.signature, pkg.id, pkg.version);\n }\n\n // Extract to node_modules (write tarball to temp file first — tar requires a path)\n const packageRoot = path.join(root, 'node_modules', pkg.id);\n await fs.mkdir(packageRoot, { recursive: true });\n const tmpFile = path.join(os.tmpdir(), `kb-registry-${Date.now()}.tgz`);\n try {\n await fs.writeFile(tmpFile, tarball);\n await extract({ file: tmpFile, cwd: packageRoot, strip: 1 });\n } finally {\n await fs.unlink(tmpFile).catch(() => { /* ignore */ });\n }\n\n return { id: pkg.id, version: pkg.version, packageRoot, integrity: actualIntegrity };\n }\n\n withShareToken(token: string): RegistryPackageSource {\n return new RegistryPackageSource({\n registryUrl: this.baseUrl,\n authToken: this.authToken,\n shareToken: token,\n signingPublicKey: this.signingPublicKey,\n });\n }\n\n async remove(_packageId: string, _root: string): Promise<void> {\n // Removal delegates to npm/fs — RegistryPackageSource doesn't manage node_modules cleanup\n throw new RegistryPackageError('UNSUPPORTED', 'Use npm source for remove operations');\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private async fetch(url: string): Promise<Response> {\n const headers: Record<string, string> = {};\n if (this.authToken) { headers['Authorization'] = `Bearer ${this.authToken}`; }\n if (this.shareToken) { headers['X-Share-Token'] = this.shareToken; }\n return globalThis.fetch(url, { headers });\n }\n\n private verifySignature(\n tarball: Buffer,\n integrity: string,\n signature: EntitySignature,\n id: string,\n version: string,\n ): void {\n if (!this.signingPublicKey) { return; } // no key = skip verification\n const payload = Buffer.from(JSON.stringify({ integrity, pkg: `${id}@${version}` }));\n const verify = createVerify('sha256');\n verify.update(payload);\n const valid = verify.verify(\n { key: this.signingPublicKey, format: 'pem' },\n Buffer.from(signature.value, 'base64'),\n );\n if (!valid) {\n throw new RegistryPackageError('SIGNATURE_INVALID', `Platform signature invalid for ${id}@${version}`);\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kb-labs/marketplace-npm",
3
3
  "description": "npm/pnpm package source for KB Labs marketplace",
4
- "version": "2.94.0",
4
+ "version": "2.98.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -18,13 +18,15 @@
18
18
  ],
19
19
  "dependencies": {
20
20
  "execa": "^9.5.3",
21
- "@kb-labs/marketplace-contracts": "2.94.0"
21
+ "tar": "^7.5.16",
22
+ "@kb-labs/marketplace-contracts": "2.98.0",
23
+ "@kb-labs/core-discovery": "2.98.0"
22
24
  },
23
25
  "devDependencies": {
24
26
  "tsup": "^8.5.0",
25
27
  "typescript": "^5",
26
- "vitest": "^3.2.4",
27
- "@kb-labs/devkit": "2.94.0"
28
+ "vitest": "^3.2.6",
29
+ "@kb-labs/devkit": "2.98.0"
28
30
  },
29
31
  "engines": {
30
32
  "node": ">=20.0.0",