artifacty 0.1.2 → 0.2.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/AGENTS.md +2 -2
- package/README.md +17 -8
- package/docs/artifact-schema-v1.md +27 -5
- package/docs/integrations.md +21 -4
- package/docs/sarif-csv-artifact-plan.md +44 -65
- package/package.json +1 -1
- package/src/cli.js +18 -4
- package/src/client/editor.js +76 -4
- package/src/lib/converters.js +429 -44
- package/src/lib/installer.js +58 -4
- package/src/lib/render.js +404 -2
- package/src/lib/storage.js +52 -3
- package/src/mcp-server.js +2 -2
- package/src/server.js +41 -2
package/src/server.js
CHANGED
|
@@ -410,12 +410,13 @@ export async function handleRequest({ request, response, store, host, port, secu
|
|
|
410
410
|
});
|
|
411
411
|
|
|
412
412
|
if (pathname.endsWith("/raw")) {
|
|
413
|
+
const raw = rawArtifactResponse(artifact);
|
|
413
414
|
response.writeHead(200, {
|
|
414
|
-
"content-type":
|
|
415
|
+
"content-type": raw.contentType,
|
|
415
416
|
"cache-control": "no-store",
|
|
416
417
|
"x-content-type-options": "nosniff"
|
|
417
418
|
});
|
|
418
|
-
response.end(headOnly ? undefined :
|
|
419
|
+
response.end(headOnly ? undefined : raw.body);
|
|
419
420
|
return;
|
|
420
421
|
}
|
|
421
422
|
|
|
@@ -514,6 +515,44 @@ export function sendHtml(response, html, statusCode = 200, headOnly = false, con
|
|
|
514
515
|
response.end(headOnly ? undefined : html);
|
|
515
516
|
}
|
|
516
517
|
|
|
518
|
+
function rawArtifactResponse(artifact) {
|
|
519
|
+
if (artifact.version.format === "image" || artifact.version.format === "video") {
|
|
520
|
+
const decoded = decodeMediaContent(artifact.content);
|
|
521
|
+
if (decoded) {
|
|
522
|
+
return {
|
|
523
|
+
body: decoded,
|
|
524
|
+
contentType: mediaContentType(artifact)
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
body: artifact.content,
|
|
530
|
+
contentType: artifact.version.contentType
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function mediaContentType(artifact) {
|
|
535
|
+
const metadataType = String(artifact.version.metadata?.mimeType || "").toLowerCase();
|
|
536
|
+
if (metadataType.startsWith("image/") || metadataType.startsWith("video/")) {
|
|
537
|
+
return metadataType;
|
|
538
|
+
}
|
|
539
|
+
const versionType = String(artifact.version.contentType || "").toLowerCase().split(";")[0];
|
|
540
|
+
if (versionType.startsWith("image/") || versionType.startsWith("video/")) {
|
|
541
|
+
return versionType;
|
|
542
|
+
}
|
|
543
|
+
return artifact.version.format === "video" ? "video/mp4" : "image/png";
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function decodeMediaContent(content) {
|
|
547
|
+
const value = String(content || "").trim();
|
|
548
|
+
const dataUrl = /^data:[^;,]+;base64,([A-Za-z0-9+/=_-\s]+)$/i.exec(value);
|
|
549
|
+
const base64 = (dataUrl ? dataUrl[1] : value).replace(/\s+/g, "").replaceAll("-", "+").replaceAll("_", "/");
|
|
550
|
+
if (!base64 || !/^[A-Za-z0-9+/]+={0,2}$/.test(base64)) {
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
return Buffer.from(base64, "base64");
|
|
554
|
+
}
|
|
555
|
+
|
|
517
556
|
function defaultContentSecurityPolicy() {
|
|
518
557
|
return [
|
|
519
558
|
"default-src 'self' data: blob:",
|