@aura3d/cli 5.0.3 → 5.1.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.
Files changed (51) hide show
  1. package/data/aura.exports.json +20 -0
  2. package/dist/commands/registry.d.ts +1 -0
  3. package/dist/commands/registry.d.ts.map +1 -1
  4. package/dist/commands/registry.js +1 -0
  5. package/dist/commands/registry.js.map +1 -1
  6. package/dist/commands/verify/index.d.ts +2 -0
  7. package/dist/commands/verify/index.d.ts.map +1 -0
  8. package/dist/commands/verify/index.js +11 -0
  9. package/dist/commands/verify/index.js.map +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/look-hdri-check.d.ts +7 -0
  14. package/dist/look-hdri-check.d.ts.map +1 -0
  15. package/dist/look-hdri-check.js +88 -0
  16. package/dist/look-hdri-check.js.map +1 -0
  17. package/dist/verify/cdp.d.ts +69 -0
  18. package/dist/verify/cdp.d.ts.map +1 -0
  19. package/dist/verify/cdp.js +223 -0
  20. package/dist/verify/cdp.js.map +1 -0
  21. package/dist/verify/codes.d.ts +17 -0
  22. package/dist/verify/codes.d.ts.map +1 -0
  23. package/dist/verify/codes.js +64 -0
  24. package/dist/verify/codes.js.map +1 -0
  25. package/dist/verify/command.d.ts +12 -0
  26. package/dist/verify/command.d.ts.map +1 -0
  27. package/dist/verify/command.js +101 -0
  28. package/dist/verify/command.js.map +1 -0
  29. package/dist/verify/frame.d.ts +46 -0
  30. package/dist/verify/frame.d.ts.map +1 -0
  31. package/dist/verify/frame.js +190 -0
  32. package/dist/verify/frame.js.map +1 -0
  33. package/dist/verify/index.d.ts +7 -0
  34. package/dist/verify/index.d.ts.map +1 -0
  35. package/dist/verify/index.js +7 -0
  36. package/dist/verify/index.js.map +1 -0
  37. package/dist/verify/lint.d.ts +43 -0
  38. package/dist/verify/lint.d.ts.map +1 -0
  39. package/dist/verify/lint.js +548 -0
  40. package/dist/verify/lint.js.map +1 -0
  41. package/dist/verify/run.d.ts +90 -0
  42. package/dist/verify/run.d.ts.map +1 -0
  43. package/dist/verify/run.js +317 -0
  44. package/dist/verify/run.js.map +1 -0
  45. package/package.json +6 -6
  46. package/skills/agent-docs/api-signatures.md +27 -4
  47. package/skills/agent-docs/recipes.md +1 -0
  48. package/skills/agent-files/AGENTS.md +1 -1
  49. package/skills/aura3d-materials-environments/SKILL.md +1 -1
  50. package/skills/aura3d-materials-environments/references/visual-helpers.md +20 -0
  51. package/skills/llms.txt +158 -29
@@ -0,0 +1,90 @@
1
+ import { type VerifyBrowserDriver } from "./cdp.js";
2
+ import { type VerifySeverity, type VerifyStepId } from "./codes.js";
3
+ import { type FrameAnalysis, type MotionAnalysis } from "./frame.js";
4
+ export declare const VERIFY_SCHEMA = "aura3d.verify/1";
5
+ export declare const VERIFY_STEP_ORDER: readonly ["typecheck", "assets", "lint", "build", "serve", "render", "inspect"];
6
+ export type VerifyStep = (typeof VERIFY_STEP_ORDER)[number];
7
+ export interface VerifyFinding {
8
+ readonly code: string;
9
+ readonly severity: VerifySeverity;
10
+ readonly step: VerifyStepId;
11
+ /** What was found, specific to this project. */
12
+ readonly message: string;
13
+ /** Plain-English cause, keyed by code (same text as `aura3d verify --explain <code>`). */
14
+ readonly cause: string;
15
+ /** Concrete fix hint, keyed by code. */
16
+ readonly fix: string;
17
+ /** `file:line:column` for source findings. */
18
+ readonly location?: {
19
+ readonly file: string;
20
+ readonly line: number;
21
+ readonly column: number;
22
+ };
23
+ readonly evidence?: Readonly<Record<string, unknown>>;
24
+ }
25
+ export interface VerifyStepResult {
26
+ readonly id: VerifyStep;
27
+ readonly status: "pass" | "fail" | "skipped";
28
+ readonly durationMs: number;
29
+ readonly detail?: string;
30
+ }
31
+ export interface VerifyReport {
32
+ readonly schema: typeof VERIFY_SCHEMA;
33
+ /** True when no finding has severity "error" and no step crashed. */
34
+ readonly ok: boolean;
35
+ readonly project: string;
36
+ readonly startedAt: string;
37
+ readonly durationMs: number;
38
+ readonly steps: readonly VerifyStepResult[];
39
+ readonly findings: readonly VerifyFinding[];
40
+ readonly counts: {
41
+ readonly error: number;
42
+ readonly warning: number;
43
+ readonly info: number;
44
+ };
45
+ /** Present when the render step produced a frame. */
46
+ readonly render?: {
47
+ readonly driver: string;
48
+ readonly viewport: {
49
+ readonly width: number;
50
+ readonly height: number;
51
+ };
52
+ readonly backend?: string;
53
+ readonly drawCalls?: number;
54
+ readonly frame?: FrameAnalysis;
55
+ readonly motion?: MotionAnalysis;
56
+ readonly degradeCodes: readonly string[];
57
+ /** File names of saved frames (relative to --frames-dir), when requested. */
58
+ readonly frames?: readonly string[];
59
+ };
60
+ }
61
+ export interface VerifyOptions {
62
+ readonly projectDir: string;
63
+ /** Steps to leave out. `inspect` needs `render`. */
64
+ readonly skip?: readonly VerifyStep[];
65
+ readonly distDir?: string;
66
+ readonly buildTimeoutMs?: number;
67
+ /** Replaces the default Chrome/CDP driver (tests inject a mock). */
68
+ readonly driver?: VerifyBrowserDriver;
69
+ readonly chromePath?: string;
70
+ readonly softwareGl?: boolean;
71
+ readonly route?: string;
72
+ readonly width?: number;
73
+ readonly height?: number;
74
+ readonly settleMs?: number;
75
+ readonly motionGapMs?: number;
76
+ readonly timeoutMs?: number;
77
+ readonly readyTimeoutMs?: number;
78
+ /** Save the two captured frames as `<project>-frame1.png` / `-frame2.png` in this directory (paths are recorded in `render.frames`). */
79
+ readonly framesDir?: string;
80
+ /** Directory used as a mutex (mkdir) around the browser steps only, so several verify runs share one GPU/browser without holding it during builds. */
81
+ readonly browserLock?: string;
82
+ /** Test seam: skip real tsc and build processes. */
83
+ readonly runCommand?: (command: string, args: readonly string[], cwd: string, timeoutMs: number) => {
84
+ status: number | null;
85
+ output: string;
86
+ };
87
+ readonly now?: () => number;
88
+ }
89
+ export declare function runVerify(options: VerifyOptions): Promise<VerifyReport>;
90
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/verify/run.ts"],"names":[],"mappings":"AAWA,OAAO,EAAsE,KAAK,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACxH,OAAO,EAAe,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACjF,OAAO,EAA0C,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAG7G,eAAO,MAAM,aAAa,oBAAoB,CAAC;AAC/C,eAAO,MAAM,iBAAiB,iFAAkF,CAAC;AACjH,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0FAA0F;IAC1F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9F,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,aAAa,CAAC;IACtC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7F,qDAAqD;IACrD,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QACvE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;QAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;QACjC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;QACzC,6EAA6E;QAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACrC,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,wIAAwI;IACxI,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sJAAsJ;IACtJ,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,oDAAoD;IACpD,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9I,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAkCD,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAsM7E"}
@@ -0,0 +1,317 @@
1
+ // `aura3d verify`: author -> validate -> render -> inspect -> correct, as one pass that ends in ONE machine-readable report.
2
+ // Steps run in a fixed order (typecheck, assets, lint, build, serve, render, inspect). Every problem becomes a finding with a stable code,
3
+ // a severity, a location (file:line for source findings), a plain-English cause and a fix hint from the explainer table in codes.ts.
4
+ // The browser is a pluggable driver (default: local Chrome over CDP, see cdp.ts); nothing here imports Playwright or the engine.
5
+ import { spawnSync } from "node:child_process";
6
+ import { existsSync, mkdirSync, readFileSync, rmdirSync, writeFileSync } from "node:fs";
7
+ import { createRequire } from "node:module";
8
+ import { basename, dirname, join, resolve } from "node:path";
9
+ import { startStaticServer } from "../deploy-browser-check.js";
10
+ import { checkDeploy, validateAssets } from "../index.js";
11
+ import { BrowserUnavailableError, createChromeCdpDriver } from "./cdp.js";
12
+ import { explainCode } from "./codes.js";
13
+ import { analyzeFrame, compareFrames, decodePng } from "./frame.js";
14
+ import { lintProject } from "./lint.js";
15
+ export const VERIFY_SCHEMA = "aura3d.verify/1";
16
+ export const VERIFY_STEP_ORDER = ["typecheck", "assets", "lint", "build", "serve", "render", "inspect"];
17
+ function defaultRunCommand(command, args, cwd, timeoutMs) {
18
+ const result = spawnSync(command, [...args], { cwd, encoding: "utf8", timeout: timeoutMs, maxBuffer: 1 << 26, env: { ...process.env, FORCE_COLOR: "0", CI: "1" } });
19
+ return { status: result.status, output: `${result.stdout ?? ""}${result.stderr ?? ""}${result.error ? `\n${result.error.message}` : ""}` };
20
+ }
21
+ function makeFinding(code, message, extra = {}) {
22
+ const entry = explainCode(code);
23
+ if (!entry)
24
+ throw new Error(`verify: code ${code} is missing from the explainer table`);
25
+ return {
26
+ code, severity: extra.severity ?? entry.severity, step: entry.step, message, cause: entry.cause, fix: entry.fix,
27
+ ...(extra.location ? { location: extra.location } : {}), ...(extra.evidence ? { evidence: extra.evidence } : {})
28
+ };
29
+ }
30
+ const tail = (text, lines = 15) => text.split("\n").filter((line) => line.trim()).slice(-lines).join("\n");
31
+ /** Nearest local `tsc`: the project's node_modules (or an ancestor's), else the TypeScript bundled with this CLI. */
32
+ function resolveTsc(projectDir) {
33
+ let dir = projectDir;
34
+ for (let i = 0; i < 8; i++) {
35
+ const candidate = join(dir, "node_modules", "typescript", "bin", "tsc");
36
+ if (existsSync(candidate))
37
+ return candidate;
38
+ const parent = dirname(dir);
39
+ if (parent === dir)
40
+ break;
41
+ dir = parent;
42
+ }
43
+ try {
44
+ return createRequire(import.meta.url).resolve("typescript/bin/tsc");
45
+ }
46
+ catch {
47
+ return undefined;
48
+ }
49
+ }
50
+ const SCENE_DEGRADED = /\b[A-Z][A-Z0-9_]*_DEGRADED\b/g;
51
+ const hasDegradeCode = (text) => /\b[A-Z][A-Z0-9_]*_DEGRADED\b/.test(text);
52
+ export async function runVerify(options) {
53
+ const now = options.now ?? Date.now;
54
+ const started = now();
55
+ const projectDir = resolve(options.projectDir);
56
+ const skip = new Set(options.skip ?? []);
57
+ const run = options.runCommand ?? defaultRunCommand;
58
+ const distDir = resolve(projectDir, options.distDir ?? "dist");
59
+ const findings = [];
60
+ const steps = [];
61
+ let render;
62
+ let capture;
63
+ let server;
64
+ let origin;
65
+ const viewport = { width: options.width ?? 960, height: options.height ?? 540 };
66
+ const step = async (id, body) => {
67
+ if (skip.has(id)) {
68
+ steps.push({ id, status: "skipped", durationMs: 0, detail: "skipped by option" });
69
+ return;
70
+ }
71
+ const before = findings.length;
72
+ const t0 = now();
73
+ let detail;
74
+ try {
75
+ detail = (await body()) || undefined;
76
+ }
77
+ catch (error) {
78
+ findings.push(makeFinding("VERIFY_STEP_CRASHED", `${id}: ${String(error.message ?? error).split("\n")[0]}`));
79
+ }
80
+ const failed = findings.slice(before).some((f) => f.severity === "error");
81
+ steps.push({ id, status: failed ? "fail" : "pass", durationMs: now() - t0, ...(detail ? { detail } : {}) });
82
+ };
83
+ // 1. typecheck ----------------------------------------------------------------------------------------------------
84
+ await step("typecheck", () => {
85
+ if (!existsSync(join(projectDir, "tsconfig.json"))) {
86
+ findings.push(makeFinding("TYPECHECK_UNAVAILABLE", "No tsconfig.json in the project."));
87
+ return "no tsconfig.json";
88
+ }
89
+ const tsc = resolveTsc(projectDir);
90
+ if (!tsc) {
91
+ findings.push(makeFinding("TYPECHECK_UNAVAILABLE", "No TypeScript compiler could be resolved."));
92
+ return "no tsc";
93
+ }
94
+ const result = run(process.execPath, [tsc, "--noEmit", "-p", "tsconfig.json", "--pretty", "false"], projectDir, 600_000);
95
+ const errors = result.output.split("\n").map((line) => /^(.+?)\((\d+),(\d+)\): error (TS\d+): (.*)$/.exec(line)).filter((m) => !!m);
96
+ for (const m of errors.slice(0, 20)) {
97
+ findings.push(makeFinding("TYPECHECK_ERROR", `${m[4]}: ${m[5]}`, { location: { file: m[1].replace(/^\.\//, ""), line: Number(m[2]), column: Number(m[3]) }, evidence: { tsCode: m[4] } }));
98
+ }
99
+ if (errors.length > 20)
100
+ findings.push(makeFinding("TYPECHECK_ERROR", `${errors.length - 20} more TypeScript errors not listed.`, { severity: "info" }));
101
+ if (result.status !== 0 && errors.length === 0)
102
+ findings.push(makeFinding("TYPECHECK_ERROR", `tsc exited ${result.status}: ${tail(result.output, 4)}`));
103
+ return `${errors.length} error(s)`;
104
+ });
105
+ // 2. assets -------------------------------------------------------------------------------------------------------
106
+ await step("assets", () => {
107
+ let detail = "no aura.assets.json";
108
+ if (existsSync(join(projectDir, "aura.assets.json"))) {
109
+ const result = validateAssets({ projectDir, source: true });
110
+ for (const failure of result.failures.slice(0, 15))
111
+ findings.push(makeFinding("ASSET_VALIDATION_FAILED", failure));
112
+ for (const warning of result.warnings.slice(0, 10))
113
+ findings.push(makeFinding("ASSET_VALIDATION_WARNING", warning));
114
+ detail = `${result.failures.length} failure(s), ${result.warnings.length} warning(s)`;
115
+ }
116
+ for (const lint of lintProject(projectDir, { group: "assets" }))
117
+ findings.push(makeFinding(lint.code, lint.message, { location: lint.location }));
118
+ return detail;
119
+ });
120
+ // 3. lint ---------------------------------------------------------------------------------------------------------
121
+ await step("lint", () => {
122
+ const results = lintProject(projectDir, { group: "scene" });
123
+ for (const lint of results)
124
+ findings.push(makeFinding(lint.code, lint.message, { location: lint.location }));
125
+ return `${results.length} finding(s)`;
126
+ });
127
+ // 4. build --------------------------------------------------------------------------------------------------------
128
+ await step("build", () => {
129
+ const pkgPath = join(projectDir, "package.json");
130
+ const pkg = existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, "utf8")) : {};
131
+ if (!pkg.scripts?.build) {
132
+ findings.push(makeFinding("BUILD_SCRIPT_MISSING", "package.json has no scripts.build."));
133
+ return;
134
+ }
135
+ const result = run("npm", ["run", "build"], projectDir, options.buildTimeoutMs ?? 600_000);
136
+ if (result.status !== 0) {
137
+ findings.push(makeFinding("BUILD_FAILED", `npm run build exited ${result.status}.`, { evidence: { outputTail: tail(result.output) } }));
138
+ return;
139
+ }
140
+ if (existsSync(join(projectDir, "aura.assets.json"))) {
141
+ const deploy = checkDeploy({ projectDir, distDir: options.distDir ?? "dist" });
142
+ for (const failure of deploy.failures.slice(0, 10))
143
+ findings.push(makeFinding("DEPLOY_CHECK_FAILED", failure));
144
+ }
145
+ });
146
+ // 5. serve --------------------------------------------------------------------------------------------------------
147
+ const wantsBrowser = !skip.has("render") || !skip.has("inspect");
148
+ await step("serve", async () => {
149
+ if (!wantsBrowser)
150
+ return "no browser step requested";
151
+ if (!existsSync(join(distDir, "index.html"))) {
152
+ findings.push(makeFinding("SERVE_FAILED", `No index.html in ${basename(distDir)}/ (did the build step run?).`));
153
+ return;
154
+ }
155
+ try {
156
+ ({ server, origin } = await startStaticServer(distDir));
157
+ return origin;
158
+ }
159
+ catch (error) {
160
+ findings.push(makeFinding("SERVE_FAILED", String(error.message ?? error)));
161
+ }
162
+ });
163
+ // 6. render -------------------------------------------------------------------------------------------------------
164
+ const lockDir = options.browserLock && wantsBrowser ? options.browserLock : undefined;
165
+ if (lockDir) {
166
+ for (;;) {
167
+ try {
168
+ mkdirSync(lockDir);
169
+ break;
170
+ }
171
+ catch (error) {
172
+ if (error.code !== "EEXIST")
173
+ throw error;
174
+ await new Promise((done) => setTimeout(done, 2_000));
175
+ }
176
+ }
177
+ }
178
+ const driver = options.driver ?? createChromeCdpDriver({ ...(options.chromePath ? { chromePath: options.chromePath } : {}), ...(options.softwareGl ? { softwareGl: true } : {}) });
179
+ try {
180
+ await step("render", async () => {
181
+ if (!origin)
182
+ return "no server";
183
+ try {
184
+ capture = await driver.capture(`${origin}${options.route ?? "/"}`, { ...viewport, settleMs: options.settleMs ?? 2_000, motionGapMs: options.motionGapMs ?? 1_000, timeoutMs: options.timeoutMs ?? 30_000, readyTimeoutMs: options.readyTimeoutMs ?? 12_000 });
185
+ }
186
+ catch (error) {
187
+ const message = String(error.message ?? error).split("\n")[0];
188
+ findings.push(makeFinding(error instanceof BrowserUnavailableError ? "BROWSER_UNAVAILABLE" : "RENDER_NAVIGATION_FAILED", message));
189
+ return;
190
+ }
191
+ if (capture.status === null || capture.status >= 400) {
192
+ findings.push(makeFinding("RENDER_NAVIGATION_FAILED", `The page answered ${capture.status ?? "no response"}.`));
193
+ return;
194
+ }
195
+ if (capture.appState.ready === undefined)
196
+ findings.push(makeFinding("RENDER_NEVER_READY", `data-aura3d-ready never appeared within ${(options.readyTimeoutMs ?? 12_000) / 1000}s of page load (tab ${capture.appState.page?.visibility ?? "?"}, ${capture.appState.page?.canvases ?? "?"} canvas, size ${capture.appState.page?.canvasSize ?? "?"}).`));
197
+ const diag = capture.appState.routeReady?.diagnostics;
198
+ const consoleText = capture.consoleMessages.map((m) => m.text).join("\n");
199
+ const allText = [consoleText, ...(diag?.errors ?? []), ...(diag?.warnings ?? [])].join("\n");
200
+ const mountText = [...capture.consoleMessages.map((m) => m.text), ...(diag?.errors ?? [])].find((t) => t.includes("RENDERER_MOUNT_FAILED"));
201
+ if (mountText || capture.appState.ready === "error") {
202
+ findings.push(makeFinding("RENDERER_MOUNT_FAILED", (mountText ?? capture.appState.error ?? "The app reported a boot error (data-aura3d-ready=error).").slice(0, 500)));
203
+ }
204
+ const degrade = [...new Set(allText.match(SCENE_DEGRADED) ?? [])].sort();
205
+ for (const code of degrade) {
206
+ const line = allText.split("\n").find((l) => l.includes(code)) ?? code;
207
+ findings.push(makeFinding(code, line.slice(0, 400), { evidence: { degradeCode: code } }));
208
+ }
209
+ render = { driver: capture.driver, viewport, ...(diag?.backend ? { backend: diag.backend } : {}), ...(diag?.drawCalls !== undefined ? { drawCalls: diag.drawCalls } : {}), degradeCodes: degrade };
210
+ return `${capture.driver}, ready=${capture.appState.ready ?? "never"}`;
211
+ });
212
+ // 7. inspect ----------------------------------------------------------------------------------------------------
213
+ await step("inspect", () => {
214
+ if (!capture)
215
+ return "no capture";
216
+ const diag = capture.appState.routeReady?.diagnostics;
217
+ const mountReported = findings.some((f) => f.code === "RENDERER_MOUNT_FAILED");
218
+ for (const text of diag?.errors ?? []) {
219
+ if (text.includes("RENDERER_MOUNT_FAILED") && mountReported)
220
+ continue;
221
+ findings.push(makeFinding("RUNTIME_DIAGNOSTIC_ERROR", text.slice(0, 500)));
222
+ }
223
+ const seenMessages = new Set();
224
+ for (const d of (diag?.degradations ?? []).slice(0, 20)) {
225
+ if (seenMessages.has(d.message))
226
+ continue;
227
+ seenMessages.add(d.message);
228
+ if (hasDegradeCode(d.message) && !/OPTION_IGNORED/.test(d.message))
229
+ continue;
230
+ const ignored = d.code === "option-ignored" || /OPTION_IGNORED/.test(d.message);
231
+ findings.push(makeFinding(ignored ? "RUNTIME_OPTION_IGNORED" : "RUNTIME_DEGRADATION", d.message.slice(0, 500), { evidence: { degradation: d.code, ...(d.nodeId ? { nodeId: d.nodeId } : {}) } }));
232
+ }
233
+ for (const text of (diag?.warnings ?? []).slice(0, 12)) {
234
+ if (hasDegradeCode(text) || seenMessages.has(text))
235
+ continue;
236
+ seenMessages.add(text);
237
+ const code = /OPTION_IGNORED/.test(text) ? "RUNTIME_OPTION_IGNORED" : /^Scene has no lights/i.test(text) ? "RUNTIME_NO_LIGHTS" : /^Scene has no interactions/i.test(text) ? "RUNTIME_NO_INTERACTIONS" : "RUNTIME_DIAGNOSTIC_WARNING";
238
+ findings.push(makeFinding(code, text.slice(0, 500)));
239
+ }
240
+ const hasHttp = capture.badResponses.length > 0 || capture.failedRequests.length > 0;
241
+ for (const message of capture.consoleMessages) {
242
+ if (message.level !== "error")
243
+ continue;
244
+ if (/RENDERER_MOUNT_FAILED/.test(message.text) && mountReported)
245
+ continue;
246
+ if (/^Failed to load resource/i.test(message.text) && hasHttp)
247
+ continue;
248
+ findings.push(makeFinding("CONSOLE_ERROR", message.text));
249
+ }
250
+ for (const text of capture.pageErrors)
251
+ findings.push(makeFinding("PAGE_EXCEPTION", text));
252
+ for (const response of capture.badResponses)
253
+ if (!/^404 \/(favicon\.ico|apple-touch-icon[^/]*\.png)$/.test(response))
254
+ findings.push(makeFinding("REQUEST_FAILED", `HTTP ${response}`));
255
+ for (const failed of capture.failedRequests.slice(0, 5))
256
+ findings.push(makeFinding("REQUEST_FAILED", `request did not complete: ${failed}`, { severity: "warning" }));
257
+ let frame;
258
+ let motion;
259
+ let savedFrames;
260
+ if (options.framesDir) {
261
+ mkdirSync(resolve(options.framesDir), { recursive: true });
262
+ savedFrames = capture.frames.map((png, index) => `${basename(projectDir)}-frame${index + 1}.png`);
263
+ capture.frames.forEach((png, index) => writeFileSync(join(resolve(options.framesDir), savedFrames[index]), png));
264
+ }
265
+ try {
266
+ const first = decodePng(capture.frames[0]);
267
+ frame = analyzeFrame(first);
268
+ const metrics = { p99Luma: frame.p99Luma, meanLuma: frame.meanLuma, distinctColors: frame.distinctColors, texturedBlockFraction: frame.texturedBlockFraction };
269
+ if (frame.black)
270
+ findings.push(makeFinding("FRAME_BLACK", `The frame is black (p99 luma ${frame.p99Luma}).`, { evidence: metrics }));
271
+ else if (frame.flat)
272
+ findings.push(makeFinding("FRAME_FLAT", `The frame is one flat colour (${frame.distinctColors} distinct colours).`, { evidence: metrics }));
273
+ else if (frame.empty)
274
+ findings.push(makeFinding("FRAME_EMPTY", `Only ${(frame.texturedBlockFraction * 100).toFixed(1)}% of the frame has scene texture.`, { evidence: metrics }));
275
+ else if (frame.dim)
276
+ findings.push(makeFinding("FRAME_DIM", `The frame is dim (p99 luma ${frame.p99Luma}).`, { evidence: metrics }));
277
+ if (capture.frames[1] && frame.drawn) {
278
+ motion = compareFrames(first, decodePng(capture.frames[1]));
279
+ if (!motion.moved)
280
+ findings.push(makeFinding("MOTION_NONE", `Two frames ${options.motionGapMs ?? 1000} ms apart are identical (${(motion.changedPixelFraction * 100).toFixed(3)}% of pixels changed).`, { evidence: { ...motion } }));
281
+ }
282
+ }
283
+ catch (error) {
284
+ findings.push(makeFinding("VERIFY_STEP_CRASHED", `frame analysis: ${String(error.message ?? error)}`));
285
+ }
286
+ if (render)
287
+ render = { ...render, ...(frame ? { frame } : {}), ...(motion ? { motion } : {}), ...(savedFrames ? { frames: savedFrames } : {}) };
288
+ return frame ? (frame.drawn ? "frame drawn" : frame.reasons.join("; ")) : "no frame";
289
+ });
290
+ }
291
+ finally {
292
+ await driver.close().catch(() => undefined);
293
+ if (lockDir) {
294
+ try {
295
+ rmdirSync(lockDir);
296
+ }
297
+ catch { /* already released */ }
298
+ }
299
+ if (server)
300
+ await new Promise((done) => server.close(() => done()));
301
+ }
302
+ const counts = { error: 0, warning: 0, info: 0 };
303
+ for (const f of findings)
304
+ counts[f.severity]++;
305
+ return {
306
+ schema: VERIFY_SCHEMA,
307
+ ok: counts.error === 0,
308
+ project: basename(projectDir),
309
+ startedAt: new Date(started).toISOString(),
310
+ durationMs: now() - started,
311
+ steps,
312
+ findings,
313
+ counts,
314
+ ...(render ? { render } : {})
315
+ };
316
+ }
317
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/verify/run.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aura3d/cli",
3
- "version": "5.0.3",
3
+ "version": "5.1.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "aura3d": "dist/cli.js",
@@ -27,11 +27,11 @@
27
27
  "main": "./dist/index.js",
28
28
  "types": "./dist/index.d.ts",
29
29
  "dependencies": {
30
- "@aura3d/asset-index": "5.0.3",
31
- "@aura3d/assets": "5.0.3",
32
- "@aura3d/audio": "5.0.3",
33
- "@aura3d/rendering": "5.0.3",
34
- "create-aura3d": "5.0.3",
30
+ "@aura3d/asset-index": "5.1.0",
31
+ "@aura3d/assets": "5.1.0",
32
+ "@aura3d/audio": "5.1.0",
33
+ "@aura3d/rendering": "5.1.0",
34
+ "create-aura3d": "5.1.0",
35
35
  "typescript": "^5.8.3"
36
36
  },
37
37
  "description": "Bring real GLB/glTF assets into Aura3D browser apps with typed references, provenance, catalog search, validation, screenshots, and deploy checks.",
@@ -58,9 +58,27 @@ const stage = scene({ background: "#101418" }).add(primitives.box({ name: "crate
58
58
  | `scene().physics(next: AuraPhysicsWorldController)` | `AuraSceneBuilder` | |
59
59
  | `scene().diagnostics(enabled?: boolean)` | `AuraSceneBuilder` | |
60
60
 
61
+ ## `camera`
62
+
63
+ `camera.frame(subject | subjects | bounds, { fill, angle, azimuth, elevation, padding, fov, aspect })` returns an orbit camera spec placed so the subject fills `fill` (default 0.6) of the limiting frame axis; `camera.frameScene(scene)` frames a whole scene (ground planes ignored); `camera.framePose(...)` returns the pose for `app.camera.setPose` on resize; `camera.worldBounds(subject)` returns the `{ min, max }` box it frames. Solved for `aspect` (default 16/9).
64
+
65
+ ```ts
66
+ void camera.frame(primitives.box({ name: "subject" }), { fill: 0.6 });
67
+ ```
68
+
69
+ | Call | Returns | Options |
70
+ |---|---|---|
71
+ | `camera.perspective(options?: Omit<AuraCameraSpec, "mode">)` | `AuraCameraSpec` | options: position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; up?: AuraVec3 \| undefined; roll?: number \| undefined; offset?: AuraVec3 \| undefined; targetOffset?: AuraVec3 \| undefined; offsetMode?: "scene" \| "target-yaw" \| …; fov?: number \| undefined; focalLength?: number \| undefined; filmGauge?: number \| undefined; filmOffset?: number \| undefined; viewOffset?: { readonly fullWidth: num…; depth?: { readonly mode?: "standa…; near?: number \| undefined; far?: number \| undefined; layers?: number \| undefined; distance… |
72
+ | `camera.orbit(options?: Omit<AuraCameraSpec, "mode">)` | `AuraCameraSpec` | same as `camera.perspective` |
73
+ | `camera.frame(subject: AuraFrameSubject \| AuraFrameBounds \| readonly AuraFrameSubject[], options?: AuraCameraFrameOptions)` | `AuraCameraSpec` | options: fill?: number \| undefined; angle?: AuraFrameAngle \| undefined; azimuth?: number \| undefined; elevation?: number \| undefined; padding?: number \| undefined; fov?: number \| undefined; aspect?: number \| undefined; minDistance?: number \| undefined; exclude?: ((node: AuraSceneNode) =>… |
74
+ | `camera.frameScene(subject: AuraFrameSubject \| readonly AuraFrameSubject[], options?: AuraCameraFrameOptions)` | `AuraCameraSpec` | same as `camera.frame` |
75
+ | `camera.worldBounds(subject: AuraFrameSubject \| readonly AuraFrameSubject[], options?: AuraCameraFrameOptions)` | `AuraFrameBounds` | same as `camera.frame` |
76
+ | `camera.framePose(subject: AuraFrameSubject \| AuraFrameBounds \| readonly AuraFrameSubject[], options?: AuraCameraFrameOptions)` | `AuraFramePose` | same as `camera.frame` |
77
+ | `camera.frameAsset(asset: AuraAssetRef<"model">, options?: AuraCameraFrameAssetOptions)` | `AuraCameraSpec` | asset: type: "model"; format: string; formatMetadata?: FormatMetadata \| undefined; url: string; hash?: string \| undefined; sha256?: string \| undefined; integrity?: AssetIntegrity \| undefined; request?: AssetRequestOptions \| und…; bounds?: AuraVec3 \| undefined; sizeBytes?: number \| undefined; optional?: boolean \| undefined; metadata?: AuraAssetMetadata \| undef…; variants?: AuraAssetVariants \| undef…; requiredDecoders?: readonly AuraAssetRequire…; lods?: readonly AuraAssetLodLeve…; colliderUrl?: string \| undefined; budget?… |
78
+
61
79
  ## `looks`
62
80
 
63
- Genre look presets: environment, key light, fog and grade in one unit. Spread `looks.appOptions(id)` into `createAuraApp` options.
81
+ Genre look presets: environment, key light, fog and grade in one unit. Spread `looks.appOptions(id)` into `createAuraApp` options. HDRI-backed presets load `/hdri/<file>.hdr` from `public/hdri/`; a missing file degrades to a procedural probe, logs one `LOOK_HDRI_MISSING` console error and lists it in `app.diagnostics().warnings`.
64
82
 
65
83
  ```ts
66
84
  void looks.preset("product-studio");
@@ -77,7 +95,7 @@ void looks.preset("product-studio");
77
95
 
78
96
  ## `material`
79
97
 
80
- Colours are CSS strings. Metals need an environment.
98
+ Colours are CSS strings (`#rgb`, `#rrggbbaa`, `rgb()`, `hsl()`, `oklch()`, names); an invalid value throws `COLOR_INVALID` naming the option. Metals need an environment.
81
99
 
82
100
  ```ts
83
101
  void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
@@ -87,6 +105,9 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
87
105
  |---|---|---|
88
106
  | `material.custom(spec: AuraCustomMaterialSpec)` | `AuraMaterialSpec` | spec: glsl: { readonly vertex?: strin…; wgsl?: { readonly vertex?: strin…; uniforms?: Readonly<Record<string, A…; uniformAliases?: Readonly<Record<string, s…; defines?: Readonly<Record<string, s…; transparent?: boolean \| undefined; depthWrite?: boolean \| undefined; depthTest?: boolean \| undefined; side?: "front" \| "back" \| "doubl…; blending?: "custom" \| "normal" \| "ad…; fog?: boolean \| undefined; patch?: Readonly<Record<string, s…; receiveShadow?: boolean \| undefined; castShadow?: boolean \| undefined |
89
107
  | `material.toon(options?: AuraToonMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; steps?: number \| undefined; ramp?: AuraAssetRef<"texture", s…; shadowColor?: string \| undefined; rim?: AuraMaterialRimSpec \| und… |
108
+ | `material.strata(options?: AuraStrataMaterialOptions)` | `AuraMaterialSpec` | options: bands?: number \| undefined; colors?: readonly string[] \| undef…; heightRange?: readonly [number, number]…; noise?: number \| undefined; noiseScale?: number \| undefined; seam?: number \| undefined; variation?: number \| undefined; slope?: AuraStrataSlopeOptions \| …; roughness?: number \| undefined; seed?: number \| undefined; side?: "front" \| "back" \| "doubl… |
109
+ | `material.caustics(options?: AuraCausticsMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; causticColor?: string \| undefined; strength?: number \| undefined; scale?: number \| undefined; speed?: number \| undefined; surfaceY?: number \| undefined; depthFade?: number \| undefined; roughness?: number \| undefined; side?: "front" \| "back" \| "doubl… |
110
+ | `material.lightBeam(options?: AuraLightBeamMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; intensity?: number \| undefined; length?: number \| undefined; falloff?: number \| undefined; edgeSoftness?: number \| undefined; noise?: number \| undefined; noiseScale?: number \| undefined; noiseSpeed?: number \| undefined |
90
111
  | `material.matcap(options: AuraMatcapMaterialOptions)` | `AuraMaterialSpec` | options: map: AuraAssetRef<"texture"> \|…; color?: string \| undefined; opacity?: number \| undefined; transparent?: boolean \| undefined; side?: "front" \| "back" \| "doubl… |
91
112
  | `material.depth(options?: AuraDepthPreviewMaterialOptions)` | `AuraMaterialSpec` | options: near?: number \| undefined; far?: number \| undefined; side?: "front" \| "back" \| "doubl… |
92
113
  | `material.normal(options?: AuraNormalMaterialOptions)` | `AuraMaterialSpec` | options: side?: "front" \| "back" \| "doubl… |
@@ -181,7 +202,7 @@ void environments.neutral({ intensity: 0.8, background: false });
181
202
  | `environments.metalStudio(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
182
203
  | `environments.glassStudio(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
183
204
  | `environments.presets()` | `readonly AuraEnvironmentMapPreset[]` | |
184
- | `environments.forMaterial(materialClass: "metal" \| "glass" \| "studio" \| "product")` | `AuraNodeBuilder<AuraEnvironmentNode>` | |
205
+ | `environments.forMaterial(materialClass: "product" \| "metal" \| "glass" \| "studio")` | `AuraNodeBuilder<AuraEnvironmentNode>` | |
185
206
 
186
207
  ## `sky`
187
208
 
@@ -235,6 +256,8 @@ void effects.bloom({ intensity: 0.7, threshold: 0.75, radius: 0.55 });
235
256
 
236
257
  | Call | Returns | Options |
237
258
  |---|---|---|
259
+ | `effects.godRays(options?: AuraGodRaysOptions)` | `AuraNodeBuilder<AuraEffectNode>` | options: name?: string \| undefined; source?: AuraVec3 \| "sun" \| AuraGo…; intensity?: number \| undefined; decay?: number \| undefined; samples?: 64 \| 32 \| 48 \| undefined; color?: string \| undefined; density?: number \| undefined; medium?: number \| undefined |
260
+ | `effects.volumetricBeam(options?: AuraVolumetricBeamOptions)` | `AuraNodeBuilder<AuraPrimitiveNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; direction?: AuraVec3 \| undefined; length?: number \| undefined; endRadius?: number \| undefined; color?: string \| undefined; intensity?: number \| undefined; falloff?: number \| undefined; edgeSoftness?: number \| undefined; noise?: number \| undefined; noiseScale?: number \| undefined; noiseSpeed?: number \| undefined; radialSegments?: number \| undefined |
238
261
  | `effects.contactShadows(options?: AuraContactShadowOptions)` | `AuraNodeBuilder<AuraEffectNode>` | options: length?: number \| undefined; thickness?: number \| undefined; steps?: 8 \| 12 \| 16 \| undefined; intensity?: number \| undefined; lights?: "sun" \| "shadowed" \| unde… |
239
262
  | `effects.toon(options?: AuraToonOptions)` | `AuraCustomPostPass` | options: name?: string \| undefined; bands?: number \| undefined; bandMix?: number \| undefined; outline?: number \| undefined; ink?: readonly [number, number,…; edgeStart?: number \| undefined; edgeEnd?: number \| undefined; size?: readonly [number, number]…; order?: AuraPostPassOrder \| undef…; inkSource?: AuraToonInkSource \| undef…; fadeDistance?: number \| undefined; inkColor?: string \| undefined |
240
263
  | `effects.pixelation(options?: AuraPixelationOptions)` | `AuraNodeBuilder<AuraEffectNode>` | options: size?: number \| undefined; name?: string \| undefined |
@@ -255,7 +278,7 @@ void effects.bloom({ intensity: 0.7, threshold: 0.75, radius: 0.55 });
255
278
  | `effects.vignette(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
256
279
  | `effects.filmGrain(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
257
280
  | `effects.chromaticAberration(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
258
- | `effects.shaderPass(options: AuraShaderPassOptions)` | `AuraCustomPostPass` | options: name?: string \| undefined; glsl?: string \| undefined; wgsl?: string \| undefined; fragment?: { readonly glsl?: string;…; uniforms?: Readonly<Record<string, n…; inputs?: readonly ("normal" \| "col…; insertAt?: PostInsertAt \| undefined; order?: AuraPostPassOrder \| undef…; enabled?: boolean \| undefined |
281
+ | `effects.shaderPass(options: AuraShaderPassOptions)` | `AuraCustomPostPass` | options: name?: string \| undefined; glsl?: string \| undefined; wgsl?: string \| undefined; fragment?: { readonly glsl?: string;…; uniforms?: Readonly<Record<string, n…; inputs?: readonly ("depth" \| "norm…; insertAt?: PostInsertAt \| undefined; order?: AuraPostPassOrder \| undef…; enabled?: boolean \| undefined |
259
282
  | `effects.fog(options?: FogNodeOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
260
283
  | `effects.cinematicBloom(options?: Omit<AuraEffectNode, "kind" \| "effect">)` | `AuraNodeBuilder<AuraEffectNode>` | |
261
284
  | `effects.volumetricFog(options?: AuraVolumetricFogOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
@@ -7,6 +7,7 @@ Page needs a `<div id="app">` (or a `<canvas id="app">`); `createAuraApp("#app",
7
7
  Rules that every recipe follows:
8
8
 
9
9
  - Orbit control: `interactions.orbit({ target: "<node name>" })`. It takes a node name, nothing else.
10
+ - Frame the subject with `camera.frame(subject, { fill: 0.6 })` (a node or array of nodes, not a hand-placed `camera.perspective`; pass `aspect` if the canvas is not 16:9).
10
11
  - Metals need something to reflect: add `environments.neutral(...)` (or a look preset) or a metallic material renders dark.
11
12
  - Instancing: `instances.cone/cylinder/sphere/box({ transforms, colors })` draws one draw call per shape; shapes are unit-sized, so `scale` sets the size.
12
13
  - A deformed surface needs vertices: build a grid with `geometry.define` and mount it with `geometry.custom(...)`.
@@ -1,3 +1,3 @@
1
1
  # Aura3D agent instructions
2
2
 
3
- Read ./llms.txt first. Read the `aura3d-art-direction` skill. Every scene gets a look. Run the look-dev loop (`aura3d look capture` → judge → iterate) before calling a scene done.
3
+ Read ./llms.txt first: its first screen holds typechecked, GPU-rendered patterns (scene skeleton, instancing, custom shader, procedural geometry, rigged models, game loop, physics, particles) to copy and adapt; do not open `node_modules` or `dist` for APIs. Read the `aura3d-art-direction` skill. Every scene gets a look. Run the look-dev loop (`aura3d look capture` → judge → iterate) before calling a scene done.
@@ -57,7 +57,7 @@ Step 1, presets (always try first):
57
57
  `dayFactor`; `weather.precipitation({ type: "rain" | "snow" })` and
58
58
  `weather.wetGround(...)`; `water.surface({ preset: "calm" | "moderate" |
59
59
  "rough" | "storm" })`. Add their `nodes` with `scene().addMany(...)` and use
60
- the returned `background`.
60
+ the returned `background`. Strata, caustics, god rays, beams: [visual helpers](references/visual-helpers.md).
61
61
  4. Choosing values: start from `material.presets()` and the named builders
62
62
  above (car paint is `material.clearcoatPaint`, thin glass is
63
63
  `material.glass`, brushed metal is `material.brushedMetal`); the in-repo
@@ -0,0 +1,20 @@
1
+ # Visual helpers: strata, caustics, god rays, light beams
2
+
3
+ Judge each in a real-GPU capture, not from the declaration.
4
+
5
+ - `material.strata({ bands, colors, heightRange, noise, noiseScale, seam, variation, slope, roughness, seed })`: height-banded
6
+ rock lit by scene lights and the environment. `slope: { angle, blend, flatColor }` gives flat ground its own colour and bands only
7
+ steep faces, so a canyon wall separates from its floor. Apply it to a `geometry.custom` heightfield (recipe 13) or any mesh. Static: it
8
+ never forces redraws.
9
+ - `material.caustics({ color, causticColor, strength, scale, speed, surfaceY, depthFade, roughness })`: a lit floor with moving
10
+ caustic light projected straight down in world X/Z. It reads `uTime`, so it animates only while the app draws frames: a
11
+ scene with a `uTime` custom shader is a declared time consumer on builds that carry `timeConsumers`; elsewhere add
12
+ `app.onFrame(() => {})` or a looping timeline.
13
+ - `effects.godRays({ source, intensity, decay, samples, color, density, medium })`: screen-space shafts (post stage S4, WebGL2
14
+ v2 chain). `source` is `"sun"` (the strongest directional light), a world point or a light-like `{ position }` / `{ direction }`.
15
+ Shafts need emitters: visible sky or a bright object near the light, and the light in front of the camera. `samples` is capped by the
16
+ quality tier, and the stage is off on the Low tier. Where the backend cannot sample depth the depth-aware chain throws
17
+ `DEPTH_POSTPROCESS_UNSUPPORTED`, as for every depth-aware post effect.
18
+ - `effects.volumetricBeam({ position, direction, length, endRadius, color, intensity, falloff, edgeSoftness, noise })`: an open
19
+ additive cone mesh (a primitive node) with a soft silhouette and drifting dust noise; two beams make a lighthouse.
20
+ `material.lightBeam(options)` is the same shader for your own cone.