motionloom 2.6.1 → 2.7.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/CHANGELOG.md +42 -0
- package/CONTRIBUTING.md +3 -1
- package/README.md +19 -3
- package/SECURITY.md +5 -5
- package/SKILL.md +6 -3
- package/agent-card.json +18 -4
- package/agent-surfaces.json +1 -1
- package/artifact-adapter-registry.json +568 -20
- package/bin/motionloom.mjs +21 -4
- package/capability-registry.json +58 -234
- package/dev-lab/public/devlab.js +36 -3
- package/dev-lab/public/index.html +6 -2
- package/docs/ACTION-SEPARATION.md +104 -0
- package/docs/ASSET-GENERATION-PLANNER.md +101 -0
- package/docs/BRANCH-PROTECTION.md +44 -0
- package/docs/EXTERNAL-CORPUS.md +26 -0
- package/docs/STATUS.md +3 -2
- package/docs/audits/field-test-after-hardening-2026-08-21.md +25 -0
- package/docs/releases/2.7.0.md +98 -0
- package/examples/agent-consumer/asset-consistency/action-sequence/envelopes/walk.00.json +32 -0
- package/examples/agent-consumer/asset-consistency/action-sequence/envelopes/walk.01.json +32 -0
- package/examples/agent-consumer/asset-consistency/action-sequence/envelopes/walk.02.json +32 -0
- package/examples/agent-consumer/asset-consistency/action-sequence/envelopes/walk.03.json +32 -0
- package/examples/agent-consumer/asset-consistency/action-sequence/hero-walk-action-manifest.json +81 -0
- package/examples/agent-consumer/asset-consistency/action-sequence/verifier-evidence/walk.00.json +26 -0
- package/examples/agent-consumer/asset-consistency/action-sequence/verifier-evidence/walk.01.json +26 -0
- package/examples/agent-consumer/asset-consistency/action-sequence/verifier-evidence/walk.02.json +26 -0
- package/examples/agent-consumer/asset-consistency/action-sequence/verifier-evidence/walk.03.json +26 -0
- package/examples/agent-consumer/asset-planning/pixellab-hero-256x448-request.json +43 -0
- package/examples/agent-consumer/devlab-live-sprite/README.md +30 -0
- package/examples/agent-consumer/devlab-live-sprite/devlab-runtime.json +61 -0
- package/examples/agent-consumer/devlab-live-sprite/frames/idle-00.png +0 -0
- package/examples/agent-consumer/devlab-live-sprite/frames/idle-01.png +0 -0
- package/examples/agent-consumer/devlab-live-sprite/frames/idle-02.png +0 -0
- package/examples/agent-consumer/devlab-live-sprite/frames/reverse-00.png +0 -0
- package/examples/agent-consumer/devlab-live-sprite/frames/reverse-01.png +0 -0
- package/examples/agent-consumer/devlab-live-sprite/frames/reverse-02.png +0 -0
- package/examples/agent-consumer/frame-generation-lock/hero-walk-lock.json +14 -1
- package/package.json +15 -1
- package/references/multi-frame-asset-generation.md +37 -2
- package/schemas/action-separation-verifier-evidence.schema.json +43 -0
- package/schemas/action-sequence-manifest.schema.json +48 -0
- package/schemas/artifact-adapter-registry.schema.json +1 -1
- package/schemas/asset-adaptation.schema.json +21 -0
- package/schemas/asset-generation-plan.schema.json +77 -0
- package/schemas/asset-generation-request.schema.json +104 -0
- package/schemas/frame-envelope.schema.json +62 -0
- package/schemas/frame-generation-lock.schema.json +24 -1
- package/scripts/action-separation.py +410 -0
- package/scripts/asset-adapt.mjs +92 -0
- package/scripts/asset-generation-plan.py +613 -0
- package/scripts/browser_review_consistency.py +64 -0
- package/scripts/fetch-project-corpus.py +91 -0
- package/scripts/frame-generation-lock.py +35 -5
- package/scripts/frame-set-preflight.py +52 -2
- package/scripts/package-consumer-smoke.mjs +20 -0
- package/scripts/quality-gate.py +7 -0
- package/scripts/release-verify.py +25 -0
- package/scripts/report-contract.py +1 -1
- package/scripts/report.py +19 -4
- package/scripts/resolve-task-bundle.py +11 -3
- package/scripts/review-hook.py +51 -2
- package/scripts/skill-doctor.py +42 -0
- package/src/output/browser-review-smoke/browser-review.json +6 -6
- package/tests/scripts/run_tests.py +45 -2
- package/tests/scripts/test_asset_adapt.py +47 -0
- package/tests/scripts/test_asset_generation_plan.py +250 -0
- package/tests/scripts/test_attestation.py +24 -8
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic target-canvas adaptation for provider outputs.
|
|
4
|
+
* This command never calls a provider and never crops or stretches silently.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import crypto from "node:crypto";
|
|
9
|
+
import { createCanvas, loadImage } from "@napi-rs/canvas";
|
|
10
|
+
|
|
11
|
+
function usage() {
|
|
12
|
+
console.error("usage: motionloom asset-adapt pad --input source.png --output target.png --width W --height H [--scale N] [--anchor center|footline] [--report report.json] [--json]");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function arg(name, fallback = undefined) {
|
|
16
|
+
const index = process.argv.indexOf(name);
|
|
17
|
+
return index >= 0 ? process.argv[index + 1] : fallback;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function required(name) {
|
|
21
|
+
const value = arg(name);
|
|
22
|
+
if (!value) throw new Error(`${name} is required`);
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function sha256(file) {
|
|
27
|
+
return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function inside(root, file) {
|
|
31
|
+
const relative = path.relative(root, file);
|
|
32
|
+
return relative && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function main() {
|
|
36
|
+
const operation = process.argv[2];
|
|
37
|
+
if (operation !== "pad") {
|
|
38
|
+
usage();
|
|
39
|
+
process.exitCode = 2;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const input = path.resolve(required("--input"));
|
|
43
|
+
const output = path.resolve(required("--output"));
|
|
44
|
+
const width = Number(required("--width"));
|
|
45
|
+
const height = Number(required("--height"));
|
|
46
|
+
const scale = Number(arg("--scale", "1"));
|
|
47
|
+
const anchor = arg("--anchor", "footline");
|
|
48
|
+
const reportPath = arg("--report");
|
|
49
|
+
if (!fs.existsSync(input)) throw new Error(`input not found: ${input}`);
|
|
50
|
+
if (!Number.isInteger(width) || width < 1 || !Number.isInteger(height) || height < 1) throw new Error("target width/height must be positive integers");
|
|
51
|
+
if (!Number.isInteger(scale) || scale < 1) throw new Error("scale must be a positive integer");
|
|
52
|
+
if (!["center", "footline"].includes(anchor)) throw new Error("anchor must be center or footline");
|
|
53
|
+
if (input === output) throw new Error("input and output must be different files");
|
|
54
|
+
const image = await loadImage(input);
|
|
55
|
+
const scaledWidth = image.width * scale;
|
|
56
|
+
const scaledHeight = image.height * scale;
|
|
57
|
+
if (scaledWidth > width || scaledHeight > height) {
|
|
58
|
+
throw new Error(`source ${image.width}x${image.height} at integer scale ${scale} exceeds target ${width}x${height}; crop is forbidden`);
|
|
59
|
+
}
|
|
60
|
+
const x = Math.floor((width - scaledWidth) / 2);
|
|
61
|
+
const y = anchor === "footline" ? height - scaledHeight : Math.floor((height - scaledHeight) / 2);
|
|
62
|
+
const canvas = createCanvas(width, height);
|
|
63
|
+
const context = canvas.getContext("2d");
|
|
64
|
+
context.clearRect(0, 0, width, height);
|
|
65
|
+
context.imageSmoothingEnabled = false;
|
|
66
|
+
context.drawImage(image, x, y, scaledWidth, scaledHeight);
|
|
67
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
68
|
+
fs.writeFileSync(output, await canvas.encode("png"));
|
|
69
|
+
const report = {
|
|
70
|
+
contract: "motionloom-asset-adaptation",
|
|
71
|
+
schema_version: "0.1",
|
|
72
|
+
operation: "pad",
|
|
73
|
+
status: "built",
|
|
74
|
+
approval: false,
|
|
75
|
+
production_approved: false,
|
|
76
|
+
source: { path: input, sha256: sha256(input), canvas: [image.width, image.height] },
|
|
77
|
+
output: { path: output, sha256: sha256(output), canvas: [width, height] },
|
|
78
|
+
transform: { scale, x, y, anchor, crop: false, stretch: false, interpolation: "nearest-neighbour" },
|
|
79
|
+
note: "Deterministic transparent canvas adaptation; visual and frame/action contracts remain required.",
|
|
80
|
+
};
|
|
81
|
+
if (reportPath) {
|
|
82
|
+
const reportAbsolute = path.resolve(reportPath);
|
|
83
|
+
fs.mkdirSync(path.dirname(reportAbsolute), { recursive: true });
|
|
84
|
+
fs.writeFileSync(reportAbsolute, JSON.stringify(report, null, 2) + "\n");
|
|
85
|
+
}
|
|
86
|
+
console.log(JSON.stringify(report, null, 2));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
main().catch((error) => {
|
|
90
|
+
console.error(`asset-adapt blocked: ${error.message}`);
|
|
91
|
+
process.exitCode = 2;
|
|
92
|
+
});
|