@prompd/core 0.5.0-beta.15 → 0.5.0-beta.17
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.cjs +107 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -37
- package/dist/index.d.ts +51 -37
- package/dist/index.js +107 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3136,12 +3136,35 @@ function isBinaryAsset(name) {
|
|
|
3136
3136
|
const dot = name.lastIndexOf(".");
|
|
3137
3137
|
return dot >= 0 && BINARY_ASSET_EXT.has(name.slice(dot + 1).toLowerCase());
|
|
3138
3138
|
}
|
|
3139
|
+
var MAX_FILE_SIZE_IN_ZIP = 10 * 1024 * 1024;
|
|
3140
|
+
var MAX_TOTAL_EXTRACTED_SIZE = 500 * 1024 * 1024;
|
|
3141
|
+
function isSymlinkEntry(perms) {
|
|
3142
|
+
return typeof perms === "number" && (perms & 61440) === 40960;
|
|
3143
|
+
}
|
|
3139
3144
|
async function extractPdpkg(buffer) {
|
|
3140
3145
|
const zip = await JSZip__default.default.loadAsync(buffer);
|
|
3141
3146
|
const out = /* @__PURE__ */ new Map();
|
|
3147
|
+
let total = 0;
|
|
3142
3148
|
for (const entry of Object.values(zip.files)) {
|
|
3149
|
+
if (entry.name.includes("\0")) {
|
|
3150
|
+
throw new Error(`Security violation: null byte in entry name: ${entry.name}`);
|
|
3151
|
+
}
|
|
3152
|
+
if (entry.name.startsWith("/") || /(^|\/)\.\.(\/|$)/.test(entry.name)) {
|
|
3153
|
+
throw new Error(`Security violation: path traversal in entry: ${entry.name}`);
|
|
3154
|
+
}
|
|
3155
|
+
if (isSymlinkEntry(entry.unixPermissions)) {
|
|
3156
|
+
throw new Error(`Security violation: symlink entry in archive: ${entry.name}`);
|
|
3157
|
+
}
|
|
3143
3158
|
if (entry.dir || isBinaryAsset(entry.name)) continue;
|
|
3144
|
-
|
|
3159
|
+
const bytes = await entry.async("uint8array");
|
|
3160
|
+
if (bytes.length > MAX_FILE_SIZE_IN_ZIP) {
|
|
3161
|
+
throw new Error(`File too large in package: ${entry.name} (${bytes.length} bytes, max ${MAX_FILE_SIZE_IN_ZIP})`);
|
|
3162
|
+
}
|
|
3163
|
+
total += bytes.length;
|
|
3164
|
+
if (total > MAX_TOTAL_EXTRACTED_SIZE) {
|
|
3165
|
+
throw new Error(`Package total decompressed size exceeds limit (${MAX_TOTAL_EXTRACTED_SIZE} bytes). Possible decompression bomb.`);
|
|
3166
|
+
}
|
|
3167
|
+
out.set(entry.name, new TextDecoder().decode(bytes));
|
|
3145
3168
|
}
|
|
3146
3169
|
return out;
|
|
3147
3170
|
}
|
|
@@ -3384,9 +3407,15 @@ function normalizeSegments(p) {
|
|
|
3384
3407
|
}
|
|
3385
3408
|
return out.join("/");
|
|
3386
3409
|
}
|
|
3387
|
-
function
|
|
3410
|
+
function slugify(name) {
|
|
3411
|
+
return name.toLowerCase().replace(/[@/]+/g, "-").replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3412
|
+
}
|
|
3413
|
+
function resolvePackageDir(root, type, name) {
|
|
3388
3414
|
return joinPosix3(root, ".prompd", getInstallDirForType(type), name);
|
|
3389
3415
|
}
|
|
3416
|
+
function resolveInstallDir(root, type, name, version) {
|
|
3417
|
+
return joinPosix3(resolvePackageDir(root, type, name), version);
|
|
3418
|
+
}
|
|
3390
3419
|
async function addWorkspaceDependency(store, root, name, version) {
|
|
3391
3420
|
if (!store.readFile) return;
|
|
3392
3421
|
const manifestPath = joinPosix3(root, "prompd.json");
|
|
@@ -3413,8 +3442,20 @@ async function removeWorkspaceDependency(store, root, name) {
|
|
|
3413
3442
|
} catch {
|
|
3414
3443
|
}
|
|
3415
3444
|
}
|
|
3445
|
+
function readManifest(files) {
|
|
3446
|
+
const raw = files.get("manifest.json") ?? files.get("prompd.json");
|
|
3447
|
+
if (!raw) return {};
|
|
3448
|
+
try {
|
|
3449
|
+
return JSON.parse(raw);
|
|
3450
|
+
} catch {
|
|
3451
|
+
return {};
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3416
3454
|
async function installPackage(opts) {
|
|
3417
|
-
|
|
3455
|
+
return installInternal(opts, /* @__PURE__ */ new Set());
|
|
3456
|
+
}
|
|
3457
|
+
async function installInternal(opts, visited) {
|
|
3458
|
+
const { ref, root, store, download, global, tools, deployTool } = opts;
|
|
3418
3459
|
const parsed = parsePackageReference(ref);
|
|
3419
3460
|
const bytes = await download(parsed.name, parsed.version);
|
|
3420
3461
|
if (bytes.length > MAX_PACKAGE_SIZE) {
|
|
@@ -3424,43 +3465,77 @@ async function installPackage(opts) {
|
|
|
3424
3465
|
if (files.size === 0) {
|
|
3425
3466
|
throw new Error(`Package "${parsed.name}" contains no installable files.`);
|
|
3426
3467
|
}
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
const
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
if (
|
|
3435
|
-
|
|
3468
|
+
const m = readManifest(files);
|
|
3469
|
+
const name = typeof m.name === "string" && m.name ? m.name : parsed.name;
|
|
3470
|
+
const version = typeof m.version === "string" && m.version ? m.version : parsed.version;
|
|
3471
|
+
const type = typeof m.type === "string" && isValidPackageType(m.type) ? m.type : opts.type && isValidPackageType(opts.type) ? opts.type : "package";
|
|
3472
|
+
visited.add(name);
|
|
3473
|
+
if (m.dependencies && typeof m.dependencies === "object") {
|
|
3474
|
+
for (const [depName, depVersion] of Object.entries(m.dependencies)) {
|
|
3475
|
+
if (depName === name || visited.has(depName)) continue;
|
|
3476
|
+
await installInternal({ ...opts, ref: `${depName}@${depVersion}`, type: void 0 }, visited);
|
|
3436
3477
|
}
|
|
3437
3478
|
}
|
|
3438
|
-
|
|
3439
|
-
const baseSegments = normalizeSegments(installedPath);
|
|
3440
|
-
await store.removeDir?.(installedPath);
|
|
3479
|
+
let installedPath;
|
|
3441
3480
|
const written = [];
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3481
|
+
if (type === "node-template") {
|
|
3482
|
+
if (!store.writeBytes) {
|
|
3483
|
+
throw new Error("Installing a node-template requires a PackageStore with writeBytes (binary). This host does not support it.");
|
|
3484
|
+
}
|
|
3485
|
+
const dir = joinPosix3(root, ".prompd", getInstallDirForType("node-template"));
|
|
3486
|
+
installedPath = joinPosix3(dir, `${slugify(name)}-${version}.pdpkg`);
|
|
3487
|
+
await store.writeBytes(installedPath, bytes);
|
|
3488
|
+
} else {
|
|
3489
|
+
installedPath = resolveInstallDir(root, type, name, version);
|
|
3490
|
+
const base = normalizeSegments(installedPath);
|
|
3491
|
+
await store.removeDir?.(installedPath);
|
|
3492
|
+
for (const [rel, content] of files) {
|
|
3493
|
+
const dest = joinPosix3(installedPath, rel);
|
|
3494
|
+
if (normalizeSegments(dest) !== base && !normalizeSegments(dest).startsWith(base + "/")) {
|
|
3495
|
+
throw new Error(`Security violation: extracted path escapes install directory: ${rel}`);
|
|
3496
|
+
}
|
|
3497
|
+
await store.writeFile(dest, content);
|
|
3498
|
+
written.push(rel);
|
|
3499
|
+
}
|
|
3500
|
+
await store.writeFile(joinPosix3(installedPath, ".prmdmeta"), JSON.stringify(m, null, 2) + "\n");
|
|
3501
|
+
if (tools && tools.length > 0) {
|
|
3502
|
+
if (type !== "skill") throw new Error(`Tool deploy is only valid for skills, but '${name}' is a ${type}.`);
|
|
3503
|
+
if (!deployTool) throw new Error("tools were requested but no deployTool hook was provided.");
|
|
3504
|
+
for (const tool of tools) await deployTool({ installedPath, name, tool });
|
|
3447
3505
|
}
|
|
3448
|
-
await store.writeFile(dest, content);
|
|
3449
|
-
written.push(rel);
|
|
3450
3506
|
}
|
|
3451
|
-
await addWorkspaceDependency(store, root, name, version);
|
|
3452
|
-
return { name, version, scope: parsed.scope, installedPath, files: written };
|
|
3507
|
+
if (!global) await addWorkspaceDependency(store, root, name, version);
|
|
3508
|
+
return { name, version, scope: parsed.scope, type, installedPath, files: written };
|
|
3453
3509
|
}
|
|
3454
3510
|
async function uninstallPackage(opts) {
|
|
3455
|
-
const {
|
|
3456
|
-
if (!store.removeDir)
|
|
3457
|
-
|
|
3511
|
+
const { root, store, global } = opts;
|
|
3512
|
+
if (!store.removeDir) throw new Error("Uninstall requires a PackageStore with removeDir.");
|
|
3513
|
+
const parsed = parsePackageReference(opts.ref);
|
|
3514
|
+
const name = parsed.name;
|
|
3515
|
+
const removed = [];
|
|
3516
|
+
for (const type of ["package", "workflow", "skill", "node-template"]) {
|
|
3517
|
+
if (type === "node-template") {
|
|
3518
|
+
const dir = joinPosix3(root, ".prompd", getInstallDirForType("node-template"));
|
|
3519
|
+
if (!store.readdir || !store.removeFile) continue;
|
|
3520
|
+
try {
|
|
3521
|
+
const prefix = `${slugify(name)}-`;
|
|
3522
|
+
for (const entry of await store.readdir(dir)) {
|
|
3523
|
+
if (entry.startsWith(prefix) && entry.endsWith(".pdpkg")) {
|
|
3524
|
+
const p = joinPosix3(dir, entry);
|
|
3525
|
+
await store.removeFile(p);
|
|
3526
|
+
removed.push(p);
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
} catch {
|
|
3530
|
+
}
|
|
3531
|
+
} else {
|
|
3532
|
+
const dir = resolvePackageDir(root, type, name);
|
|
3533
|
+
await store.removeDir(dir);
|
|
3534
|
+
removed.push(dir);
|
|
3535
|
+
}
|
|
3458
3536
|
}
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
await store.removeDir(installedPath);
|
|
3462
|
-
await removeWorkspaceDependency(store, root, parsed.name);
|
|
3463
|
-
return { name: parsed.name, scope: parsed.scope, installedPath };
|
|
3537
|
+
if (!global) await removeWorkspaceDependency(store, root, name);
|
|
3538
|
+
return { name, scope: parsed.scope, removed };
|
|
3464
3539
|
}
|
|
3465
3540
|
|
|
3466
3541
|
// src/lib/compiler/index.ts
|