@starci/hfs 4.8.0 → 4.9.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 (144) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/emit/operation-schemas.mjs +3 -0
  3. package/lint/bound-sys.cjs +2 -2
  4. package/package.json +1 -1
  5. package/runtime/engine/admission.mjs +1 -1
  6. package/runtime/engine/config.mjs +2 -2
  7. package/runtime/engine/db/machine.mjs +2 -3
  8. package/runtime/engine/invalid-config.mjs +1 -1
  9. package/runtime/knowledge/hfs/canon-pins.yaml +11 -10
  10. package/runtime/knowledge/hfs/rules.yaml +23 -0
  11. package/runtime/modules/kernel/failure-codes.yaml +10 -0
  12. package/runtime/scripts/guards/git-policy.mjs +2 -3
  13. package/runtime/scripts/hfs/rules/pipeline.mjs +5 -5
  14. package/runtime/scripts/lib/package-at.mjs +5 -6
  15. package/runtime/scripts/lib/roots.mjs +2 -2
  16. package/runtime/scripts/work/draw/draw-dna-findings.mjs +23 -12
  17. package/runtime/scripts/work/draw/draw-dna.mjs +14 -8
  18. package/scaffold/app.mjs +4 -4
  19. package/src/main.mjs +3 -3
  20. package/sync/index.mjs +14 -1
  21. package/sync/managed.mjs +3 -2
  22. package/templates/app/codegen-graphql.mjs.partial +4 -4
  23. package/templates/app/quality-config/sonar-project.properties +4 -2
  24. package/runtime/engine/canonical-json.mjs +0 -14
  25. package/runtime/engine/db/ledger-open.mjs +0 -38
  26. package/runtime/engine/db/ledger-paths.mjs +0 -87
  27. package/runtime/engine/db/ledger.mjs +0 -1068
  28. package/runtime/engine/db/provision-authority.mjs +0 -76
  29. package/runtime/engine/db/provision-operations.mjs +0 -146
  30. package/runtime/packages/grammar/scripts/build-stamp.mjs +0 -98
  31. package/runtime/scripts/agent/codex-app-server.mjs +0 -79
  32. package/runtime/scripts/agent/codex-mirror-source.mjs +0 -34
  33. package/runtime/scripts/agent/host-agents.mjs +0 -80
  34. package/runtime/scripts/agent/launch-trust-policy.mjs +0 -53
  35. package/runtime/scripts/agent/trust-launch.mjs +0 -191
  36. package/runtime/scripts/agent/trust.mjs +0 -523
  37. package/runtime/scripts/api/fs/temp-path.mjs +0 -8
  38. package/runtime/scripts/api/git/diff-names.mjs +0 -8
  39. package/runtime/scripts/api/git/head-time.mjs +0 -10
  40. package/runtime/scripts/api/git/index-lock.mjs +0 -78
  41. package/runtime/scripts/api/git/log.mjs +0 -5
  42. package/runtime/scripts/api/git/merge-tree.mjs +0 -5
  43. package/runtime/scripts/api/git/rev-list.mjs +0 -5
  44. package/runtime/scripts/api/git/rev-parse.mjs +0 -13
  45. package/runtime/scripts/api/git/status-query.mjs +0 -5
  46. package/runtime/scripts/api/git/worktree-list-query.mjs +0 -5
  47. package/runtime/scripts/api/node/spawn-node.mjs +0 -6
  48. package/runtime/scripts/api/npm/dist-tag.mjs +0 -8
  49. package/runtime/scripts/api/npm/lib.mjs +0 -32
  50. package/runtime/scripts/api/npm/pack-dry-run.mjs +0 -9
  51. package/runtime/scripts/api/npm/run-npm.mjs +0 -6
  52. package/runtime/scripts/api/npm/view.mjs +0 -30
  53. package/runtime/scripts/api/npm/whoami.mjs +0 -8
  54. package/runtime/scripts/api/process/hide-child-windows.mjs +0 -31
  55. package/runtime/scripts/api/process/kill-tree.mjs +0 -22
  56. package/runtime/scripts/api/process/lib.mjs +0 -125
  57. package/runtime/scripts/api/process/probe-guard-command.mjs +0 -74
  58. package/runtime/scripts/api/process/process-list.mjs +0 -28
  59. package/runtime/scripts/api/process/system-tool.mjs +0 -37
  60. package/runtime/scripts/gates/canon-digest.mjs +0 -81
  61. package/runtime/scripts/gates/canon-pins.mjs +0 -119
  62. package/runtime/scripts/gates/gate-input.mjs +0 -98
  63. package/runtime/scripts/gates/gate.mjs +0 -636
  64. package/runtime/scripts/gates/grammar-dist.mjs +0 -206
  65. package/runtime/scripts/gates/output.mjs +0 -40
  66. package/runtime/scripts/gates/runtime-package-selection.mjs +0 -27
  67. package/runtime/scripts/gates/test-world-run.mjs +0 -252
  68. package/runtime/scripts/gates/type-impact.mjs +0 -277
  69. package/runtime/scripts/gates/type-program.mjs +0 -113
  70. package/runtime/scripts/guards/assigned-command.mjs +0 -12
  71. package/runtime/scripts/guards/call-rights.mjs +0 -47
  72. package/runtime/scripts/guards/command-guard.mjs +0 -468
  73. package/runtime/scripts/guards/command-policy.mjs +0 -342
  74. package/runtime/scripts/guards/critic-reach.mjs +0 -82
  75. package/runtime/scripts/guards/deps-guard.mjs +0 -150
  76. package/runtime/scripts/guards/env-dump-verdict.mjs +0 -89
  77. package/runtime/scripts/guards/guards-root.mjs +0 -27
  78. package/runtime/scripts/guards/hook-install.mjs +0 -343
  79. package/runtime/scripts/guards/hook-verdict.mjs +0 -12
  80. package/runtime/scripts/guards/install-verdict.mjs +0 -41
  81. package/runtime/scripts/guards/kernel-seat.mjs +0 -63
  82. package/runtime/scripts/guards/launch-verdict.mjs +0 -74
  83. package/runtime/scripts/guards/op-starci.mjs +0 -16
  84. package/runtime/scripts/guards/op-write-scope.mjs +0 -27
  85. package/runtime/scripts/guards/orca-self-lifecycle.mjs +0 -39
  86. package/runtime/scripts/guards/process-kill-verdict.mjs +0 -106
  87. package/runtime/scripts/guards/protected-zone.mjs +0 -56
  88. package/runtime/scripts/guards/refusals.mjs +0 -27
  89. package/runtime/scripts/guards/rights.mjs +0 -268
  90. package/runtime/scripts/guards/seat-tools.mjs +0 -47
  91. package/runtime/scripts/guards/shell-commands.mjs +0 -328
  92. package/runtime/scripts/guards/supervisor-seat.mjs +0 -80
  93. package/runtime/scripts/guards/verdict-process.mjs +0 -81
  94. package/runtime/scripts/lib/app-data.mjs +0 -11
  95. package/runtime/scripts/lib/canon-binding-text.mjs +0 -129
  96. package/runtime/scripts/lib/check-format.mjs +0 -7
  97. package/runtime/scripts/lib/failure-code-reads.mjs +0 -100
  98. package/runtime/scripts/lib/failure-code-template-errors.mjs +0 -161
  99. package/runtime/scripts/lib/git-dir.mjs +0 -120
  100. package/runtime/scripts/lib/guard-command.mjs +0 -24
  101. package/runtime/scripts/lib/interrupt-cleanup.mjs +0 -38
  102. package/runtime/scripts/lib/json-formats.mjs +0 -34
  103. package/runtime/scripts/lib/managed-json.mjs +0 -51
  104. package/runtime/scripts/lib/menu-parts.mjs +0 -11
  105. package/runtime/scripts/lib/op-gate.mjs +0 -15
  106. package/runtime/scripts/lib/provision-control-failure.mjs +0 -32
  107. package/runtime/scripts/lib/release-version.mjs +0 -134
  108. package/runtime/scripts/lib/runtime-location.mjs +0 -21
  109. package/runtime/scripts/lib/runtime-package-inventory.mjs +0 -21
  110. package/runtime/scripts/lib/runtime-package-manifests.mjs +0 -84
  111. package/runtime/scripts/lib/runtime-publication-tags.mjs +0 -56
  112. package/runtime/scripts/lib/seat-env.mjs +0 -43
  113. package/runtime/scripts/lib/sonar-new-code.mjs +0 -58
  114. package/runtime/scripts/lib/source-package-imports.mjs +0 -111
  115. package/runtime/scripts/lib/tar-files.mjs +0 -39
  116. package/runtime/scripts/lib/uat-run-folder.mjs +0 -118
  117. package/runtime/scripts/lib/worktree-record.mjs +0 -14
  118. package/runtime/scripts/machine/ask-receipts.mjs +0 -113
  119. package/runtime/scripts/machine/evidence-store.mjs +0 -223
  120. package/runtime/scripts/machine/home.mjs +0 -216
  121. package/runtime/scripts/machine/host-lock.mjs +0 -184
  122. package/runtime/scripts/machine/lock-recovery.mjs +0 -55
  123. package/runtime/scripts/machine/roles-contract.mjs +0 -90
  124. package/runtime/scripts/machine/runtime-change.mjs +0 -20
  125. package/runtime/scripts/machine/workflow-tree.mjs +0 -122
  126. package/runtime/scripts/reconciler/checklist-items.mjs +0 -7
  127. package/runtime/scripts/reconciler/guard-row.mjs +0 -25
  128. package/runtime/scripts/work/brand/brand-colour.mjs +0 -125
  129. package/runtime/scripts/work/brand/brand-tokens.mjs +0 -167
  130. package/runtime/scripts/work/brand/brand-value-source.mjs +0 -106
  131. package/runtime/scripts/work/brand/brand.mjs +0 -892
  132. package/runtime/scripts/work/draw/draw-layer.mjs +0 -306
  133. package/runtime/scripts/work/draw/draw-rationale-page.mjs +0 -237
  134. package/runtime/scripts/work/draw/draw-rationale-ref.mjs +0 -14
  135. package/runtime/scripts/work/draw/draw-rationale.mjs +0 -353
  136. package/runtime/scripts/work/draw/draw-source.mjs +0 -412
  137. package/runtime/scripts/work/draw/draw-taste.mjs +0 -278
  138. package/runtime/scripts/work/draw/draw-typecheck.mjs +0 -72
  139. package/runtime/scripts/work/draw/rationale-findings.mjs +0 -205
  140. package/runtime/scripts/work/draw-grammar.mjs +0 -175
  141. package/runtime/scripts/work/draw-render-page.mjs +0 -188
  142. package/runtime/scripts/work/draw-render.mjs +0 -626
  143. package/runtime/scripts/work/png.mjs +0 -237
  144. package/runtime/scripts/work/render-tools.mjs +0 -64
@@ -1,237 +0,0 @@
1
- // png.mjs — the PNG reader and writer the layout-tree tooling composes with.
2
- //
3
- // No dependency: `node:zlib` inflates and deflates, and `zlib.crc32` (Node >= 22.2) stamps the chunks. It
4
- // exists so a composite direction is a deterministic function of its inputs on every host - a consumer
5
- // install has no sharp, and a native image library would make the same inputs produce different bytes on
6
- // two machines. What it reads: bit depths 1/2/4/8/16, colour types 0 (greyscale), 2 (truecolour), 3
7
- // (palette, with tRNS), 4 (greyscale + alpha) and 6 (truecolour + alpha), non-interlaced. Everything else
8
- // throws `unsupported png: <why>` rather than guessing. Every image is handed back as 8-bit RGBA.
9
- import zlib from 'node:zlib';
10
-
11
- const SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
12
- const CHANNELS = { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4 };
13
-
14
- const paeth = (a, b, c) => {
15
- const p = a + b - c, pa = Math.abs(p - a), pb = Math.abs(p - b), pc = Math.abs(p - c);
16
- if (pa <= pb && pa <= pc) return a;
17
- return pb <= pc ? b : c;
18
- };
19
-
20
- function pngChunks(buf) {
21
- let header = null, palette = null, trns = null;
22
- const idat = [];
23
- for (let at = 8; at + 8 <= buf.length;) {
24
- const length = buf.readUInt32BE(at), type = buf.toString('latin1', at + 4, at + 8), start = at + 8;
25
- if (start + length + 4 > buf.length) throw new Error(`unsupported png: chunk ${type} runs past the end of the file`);
26
- const body = buf.subarray(start, start + length);
27
- if (type === 'IHDR') header = { width: body.readUInt32BE(0), height: body.readUInt32BE(4), depth: body[8], colour: body[9], interlace: body[12] };
28
- else if (type === 'PLTE') palette = body;
29
- else if (type === 'tRNS') trns = body;
30
- else if (type === 'IDAT') idat.push(body);
31
- else if (type === 'IEND') break;
32
- at = start + length + 4;
33
- }
34
- return { header, palette, trns, idat };
35
- }
36
-
37
- function sampleAt(line, depth, index) {
38
- if (depth === 8) return line[index];
39
- if (depth === 16) return line[index * 2];
40
- const bit = index * depth, byte = line[bit >> 3], shift = 8 - depth - (bit & 7);
41
- return (byte >> shift) & ((1 << depth) - 1);
42
- }
43
-
44
- function unfilterRow(raw, at, line, prior, filter, bpp) {
45
- for (let i = 0; i < line.length; i += 1) {
46
- const x = raw[at + 1 + i], a = i >= bpp ? line[i - bpp] : 0, b = prior[i], c = i >= bpp ? prior[i - bpp] : 0;
47
- let value = x;
48
- if (filter === 1) value += a;
49
- else if (filter === 2) value += b;
50
- else if (filter === 3) value += (a + b) >> 1;
51
- else if (filter === 4) value += paeth(a, b, c);
52
- line[i] = value & 0xff;
53
- }
54
- }
55
-
56
- function writePixel(out, offset, sample, colour, scale, palette, trns) {
57
- if (colour === 3) {
58
- const index = sample(0);
59
- out[offset] = palette[index * 3] ?? 0; out[offset + 1] = palette[index * 3 + 1] ?? 0; out[offset + 2] = palette[index * 3 + 2] ?? 0;
60
- out[offset + 3] = trns && index < trns.length ? trns[index] : 255;
61
- } else if (colour === 0 || colour === 4) {
62
- const g = Math.round(sample(0) * scale);
63
- out[offset] = g; out[offset + 1] = g; out[offset + 2] = g; out[offset + 3] = colour === 4 ? sample(1) : 255;
64
- } else {
65
- out[offset] = sample(0); out[offset + 1] = sample(1); out[offset + 2] = sample(2); out[offset + 3] = colour === 6 ? sample(3) : 255;
66
- }
67
- }
68
-
69
- function decodeRows(raw, header, palette, trns, channels, stride, bpp) {
70
- const { width, height, depth, colour } = header;
71
- if (raw.length < (stride + 1) * height) throw new Error('unsupported png: image data shorter than the header declares');
72
- const out = new Uint8Array(width * height * 4);
73
- let prior = new Uint8Array(stride);
74
- const line = new Uint8Array(stride);
75
- const scale = depth < 8 && colour !== 3 ? 255 / ((1 << depth) - 1) : 1;
76
- for (let y = 0; y < height; y += 1) {
77
- const at = y * (stride + 1), filter = raw[at];
78
- if (filter > 4) throw new Error(`unsupported png: filter type ${filter}`);
79
- unfilterRow(raw, at, line, prior, filter, bpp);
80
- const sample = (index) => sampleAt(line, depth, index);
81
- for (let x = 0; x < width; x += 1) writePixel(out, (y * width + x) * 4, (index) => sample(x * channels + index), colour, scale, palette, trns);
82
- prior = Uint8Array.from(line);
83
- }
84
- return out;
85
- }
86
-
87
- /** Decode PNG bytes into {width, height, data: Uint8Array RGBA}. Throws `unsupported png: ...`. */
88
- export function decodePng(bytes) {
89
- const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes ?? []);
90
- if (buf.length < 8 || !buf.subarray(0, 8).equals(SIGNATURE)) throw new Error('unsupported png: not a PNG signature');
91
- const { header, palette, trns, idat } = pngChunks(buf);
92
- if (!header?.width || !header?.height) throw new Error('unsupported png: no usable IHDR');
93
- const { width, height, depth, colour } = header;
94
- if (!(colour in CHANNELS)) throw new Error(`unsupported png: colour type ${colour}`);
95
- if (![1, 2, 4, 8, 16].includes(depth) || ((colour === 2 || colour === 4 || colour === 6) && depth < 8) || (colour === 3 && depth > 8)) throw new Error(`unsupported png: bit depth ${depth} for colour type ${colour}`);
96
- if (header.interlace !== 0) throw new Error('unsupported png: interlaced (Adam7) images are not read');
97
- if (colour === 3 && !palette) throw new Error('unsupported png: palette image without PLTE');
98
- let raw;
99
- try { raw = zlib.inflateSync(Buffer.concat(idat)); } catch (error) { throw new Error(`unsupported png: image data does not inflate (${error.message})`); }
100
- const channels = CHANNELS[colour];
101
- const bitsPerPixel = channels * depth;
102
- const stride = Math.ceil((width * bitsPerPixel) / 8);
103
- const bpp = Math.max(1, bitsPerPixel >> 3);
104
- return { width, height, data: decodeRows(raw, header, palette, trns, channels, stride, bpp) };
105
- }
106
-
107
- const chunk = (type, body) => {
108
- const head = Buffer.alloc(8);
109
- head.writeUInt32BE(body.length, 0);
110
- head.write(type, 4, 'latin1');
111
- const crc = Buffer.alloc(4);
112
- crc.writeUInt32BE(zlib.crc32(Buffer.concat([Buffer.from(type, 'latin1'), body])) >>> 0, 0);
113
- return Buffer.concat([head, body, crc]);
114
- };
115
-
116
- /** Encode {width, height, data RGBA} as PNG: truecolour when fully opaque, else truecolour + alpha. Filter 0, fixed deflate level. */
117
- export function encodePng({ width, height, data }) {
118
- let opaque = true;
119
- for (let i = 3; i < data.length; i += 4) if (data[i] !== 255) { opaque = false; break; }
120
- const channels = opaque ? 3 : 4, stride = width * channels;
121
- const raw = Buffer.alloc((stride + 1) * height);
122
- for (let y = 0; y < height; y += 1) {
123
- const row = y * (stride + 1);
124
- raw[row] = 0;
125
- for (let x = 0; x < width; x += 1) {
126
- const s = (y * width + x) * 4, d = row + 1 + x * channels;
127
- raw[d] = data[s]; raw[d + 1] = data[s + 1]; raw[d + 2] = data[s + 2];
128
- if (!opaque) raw[d + 3] = data[s + 3];
129
- }
130
- }
131
- const ihdr = Buffer.alloc(13);
132
- ihdr.writeUInt32BE(width, 0); ihdr.writeUInt32BE(height, 4);
133
- ihdr[8] = 8; ihdr[9] = opaque ? 2 : 6; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0;
134
- return Buffer.concat([SIGNATURE, chunk('IHDR', ihdr), chunk('IDAT', zlib.deflateSync(raw, { level: 9 })), chunk('IEND', Buffer.alloc(0))]);
135
- }
136
-
137
- /** A blank RGBA image filled with one colour [r, g, b, a]. */
138
- export function blankImage(width, height, rgba = [255, 255, 255, 255]) {
139
- const data = new Uint8Array(width * height * 4);
140
- for (let i = 0; i < data.length; i += 4) { data[i] = rgba[0]; data[i + 1] = rgba[1]; data[i + 2] = rgba[2]; data[i + 3] = rgba[3]; }
141
- return { width, height, data };
142
- }
143
-
144
- /**
145
- * Resample `image` to width x height. Downscaling by more than half averages the covered source area (a box
146
- * filter), everything else is bilinear. Pure arithmetic, so the same inputs give the same pixels anywhere.
147
- */
148
- function resizeBox(image, width, height, sx, sy, src, out) {
149
- for (let y = 0; y < height; y += 1) {
150
- const y0 = Math.floor(y * sy), y1 = Math.max(y0 + 1, Math.floor((y + 1) * sy));
151
- for (let x = 0; x < width; x += 1) {
152
- const x0 = Math.floor(x * sx), x1 = Math.max(x0 + 1, Math.floor((x + 1) * sx));
153
- const sum = [0, 0, 0, 0];
154
- for (let yy = y0; yy < y1; yy += 1) for (let xx = x0; xx < x1; xx += 1) { const s = (yy * image.width + xx) * 4; sum[0] += src[s]; sum[1] += src[s + 1]; sum[2] += src[s + 2]; sum[3] += src[s + 3]; }
155
- const n = (y1 - y0) * (x1 - x0), d = (y * width + x) * 4;
156
- for (let c = 0; c < 4; c += 1) out[d + c] = Math.round(sum[c] / n);
157
- }
158
- }
159
- return { width, height, data: out };
160
- }
161
-
162
- function resizeBilinear(image, width, height, sx, sy, src, out) {
163
- for (let y = 0; y < height; y += 1) {
164
- const fy = Math.min(image.height - 1, Math.max(0, (y + 0.5) * sy - 0.5)), y0 = Math.floor(fy), y1 = Math.min(image.height - 1, y0 + 1), wy = fy - y0;
165
- for (let x = 0; x < width; x += 1) {
166
- const fx = Math.min(image.width - 1, Math.max(0, (x + 0.5) * sx - 0.5)), x0 = Math.floor(fx), x1 = Math.min(image.width - 1, x0 + 1), wx = fx - x0;
167
- const a = (y0 * image.width + x0) * 4, b = (y0 * image.width + x1) * 4, c = (y1 * image.width + x0) * 4, e = (y1 * image.width + x1) * 4, d = (y * width + x) * 4;
168
- for (let k = 0; k < 4; k += 1) out[d + k] = Math.round((src[a + k] * (1 - wx) + src[b + k] * wx) * (1 - wy) + (src[c + k] * (1 - wx) + src[e + k] * wx) * wy);
169
- }
170
- }
171
- return { width, height, data: out };
172
- }
173
-
174
- export function resizeImage(image, width, height) {
175
- if (image.width === width && image.height === height) return { width, height, data: Uint8Array.from(image.data) };
176
- const out = new Uint8Array(width * height * 4), sx = image.width / width, sy = image.height / height, src = image.data;
177
- if (sx > 2 || sy > 2) return resizeBox(image, width, height, sx, sy, src, out);
178
- return resizeBilinear(image, width, height, sx, sy, src, out);
179
- }
180
-
181
- /** Crop a rectangle out of `image`. */
182
- export function cropImage(image, { x, y, width, height }) {
183
- const out = new Uint8Array(width * height * 4);
184
- for (let row = 0; row < height; row += 1) {
185
- const s = ((y + row) * image.width + x) * 4;
186
- out.set(image.data.subarray(s, s + width * 4), row * width * 4);
187
- }
188
- return { width, height, data: out };
189
- }
190
-
191
- function compositePixel(base, top, source, target, alphaByte) {
192
- if (alphaByte === 255) { base.data[target] = top.data[source]; base.data[target + 1] = top.data[source + 1]; base.data[target + 2] = top.data[source + 2]; base.data[target + 3] = 255; return; }
193
- if (alphaByte === 0) return;
194
- const alpha = alphaByte / 255, beneath = base.data[target + 3] / 255, outA = alpha + beneath * (1 - alpha);
195
- for (let c = 0; c < 3; c += 1) base.data[target + c] = Math.round((top.data[source + c] * alpha + base.data[target + c] * beneath * (1 - alpha)) / (outA || 1));
196
- base.data[target + 3] = Math.round(outA * 255);
197
- }
198
-
199
- /** Alpha-composite `top` over `base` (mutated) with its top-left corner at (x, y); pixels outside `base` are dropped. */
200
- export function drawOver(base, top, x, y) {
201
- for (let row = 0; row < top.height; row += 1) {
202
- const by = y + row;
203
- if (by < 0 || by >= base.height) continue;
204
- for (let col = 0; col < top.width; col += 1) {
205
- const bx = x + col;
206
- if (bx < 0 || bx >= base.width) continue;
207
- const s = (row * top.width + col) * 4, d = (by * base.width + bx) * 4;
208
- compositePixel(base, top, s, d, top.data[s + 3]);
209
- }
210
- }
211
- return base;
212
- }
213
-
214
- /** Darken every pixel of `image` (mutated) by a black scrim of opacity `amount` in [0, 1]. */
215
- export function dimImage(image, amount) {
216
- const keep = 1 - amount;
217
- for (let i = 0; i < image.data.length; i += 4) for (let c = 0; c < 3; c += 1) image.data[i + c] = Math.round(image.data[i + c] * keep);
218
- return image;
219
- }
220
-
221
- /**
222
- * The bounding rectangle of the pixels matching `key` ([r, g, b], within `tolerance` per channel), and how much
223
- * of that rectangle they fill. A capture or a drawn layout marks its page slot with one flat key colour, so the
224
- * slot is measured from the image rather than typed in.
225
- */
226
- export function keyRect(image, key = [255, 0, 255], tolerance = 8) {
227
- let minX = Infinity, minY = Infinity, maxX = -1, maxY = -1, count = 0;
228
- const hits = (i) => Math.abs(image.data[i] - key[0]) <= tolerance && Math.abs(image.data[i + 1] - key[1]) <= tolerance && Math.abs(image.data[i + 2] - key[2]) <= tolerance && image.data[i + 3] > 200;
229
- for (let y = 0; y < image.height; y += 1) for (let x = 0; x < image.width; x += 1) {
230
- if (!hits((y * image.width + x) * 4)) continue;
231
- count += 1;
232
- minX = Math.min(minX, x); maxX = Math.max(maxX, x); minY = Math.min(minY, y); maxY = Math.max(maxY, y);
233
- }
234
- if (!count) return null;
235
- const rect = { x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1 };
236
- return { rect, fill: count / (rect.width * rect.height) };
237
- }
@@ -1,64 +0,0 @@
1
- // render-tools.mjs - the tools a capture needs (Playwright with a browser, esbuild) and where they come from.
2
- //
3
- // A product declares neither: a drawing is captured by the runtime, so the runtime pins and installs both (package.json devDependencies,
4
- // the browser download Playwright keeps in its own cache). The product's own install, when it has one, comes first (its tailwindcss,
5
- // React and tsconfig paths resolve from the product regardless); the runtime's install is always the last candidate. This is the same
6
- // order layout-render.mjs uses for its capture. A host with neither a project install nor the runtime's, or with Playwright and no browser,
7
- // is RENDER_TOOL_UNAVAILABLE: one typed host fact the dispatch of an op that needs a capture checks before it spends an attempt.
8
- import fs from 'node:fs';
9
- import { skillRoot } from '../../engine/runtime-root.mjs';
10
- import { findPackage, requirePackage } from '../lib/package-at.mjs';
11
- import { toolSearchDirs } from '../lib/roots.mjs';
12
-
13
- /** The catalogued code of a host that cannot take a capture (modules/kernel/failure-codes.yaml). */
14
- export const RENDER_TOOL_UNAVAILABLE = 'RENDER_TOOL_UNAVAILABLE';
15
- /** The hint an op manifest carries in route.riskHints when its steps capture a render. */
16
- export const RENDER_CAPABILITY_HINT = 'host-capability-required:render';
17
- const PLAYWRIGHT_PACKAGES = Object.freeze(['playwright', '@playwright/test', 'playwright-core']);
18
-
19
- /** The first install of `names` resolvable from each of `dirs` in turn, one per distinct package root. */
20
- function installsOf(dirs, names) {
21
- const seen = new Set();
22
- const out = [];
23
- for (const dir of dirs) {
24
- const found = findPackage([dir], names);
25
- if (found && !seen.has(found.root)) { seen.add(found.root); out.push(found); }
26
- }
27
- return out;
28
- }
29
-
30
- /** Whether the browser a Playwright install would launch is on the disk. */
31
- function browserOf(found) {
32
- try { return fs.existsSync(requirePackage(found).chromium.executablePath()); } catch { return false; }
33
- }
34
-
35
- /**
36
- * The Playwright install a capture launches: the first of the project's and the runtime's whose browser exists, else the first install
37
- * (its launch then names the missing browser). Null when no directory resolves one.
38
- */
39
- export function playwrightInstall(dirs, { runtime = skillRoot } = {}) {
40
- const installs = installsOf(toolSearchDirs(dirs, runtime), PLAYWRIGHT_PACKAGES);
41
- return installs.find(browserOf) ?? installs[0] ?? null;
42
- }
43
-
44
- /** The esbuild install a capture bundles with: the project's, else the runtime's; null when neither resolves one. */
45
- export const esbuildInstall = (dirs, { runtime = skillRoot } = {}) => findPackage(toolSearchDirs(dirs, runtime), ['esbuild']);
46
-
47
- /**
48
- * What this host can capture with, from `dirs` (the tree the op runs in) and the runtime: {ok, missing: [{tool, why}], playwright, esbuild}.
49
- * `missing` names each absent tool with the one-line reason a Kernel and the owner read.
50
- */
51
- export function renderToolStatus(dirs = [], { runtime = skillRoot } = {}) {
52
- const playwright = playwrightInstall(dirs, { runtime });
53
- const esbuild = esbuildInstall(dirs, { runtime });
54
- const missing = [];
55
- if (!playwright) missing.push({ tool: 'playwright', why: 'no Playwright install resolves from the project or the runtime' });
56
- else if (!browserOf(playwright)) missing.push({ tool: 'chromium', why: `Playwright ${playwright.version} resolves at ${playwright.root.split(/[\\/]/).slice(-2).join('/')} and its Chromium download is absent from the browser cache` });
57
- if (!esbuild) missing.push({ tool: 'esbuild', why: 'no esbuild install resolves from the project or the runtime' });
58
- return { ok: missing.length === 0, missing, playwright, esbuild };
59
- }
60
-
61
- /** The one-line instruction for a missing tool: what provisions it. */
62
- export const renderToolFix = (missing) => missing.map((item) => (item.tool === 'chromium'
63
- ? 'run `npx playwright install chromium` once on the host (the browser cache is the owner\'s to fill)'
64
- : `install the runtime's dependencies (\`npm install\` in the runtime checkout) so ${item.tool} resolves`)).join('; ');