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