clanka 0.5.2 → 0.6.1
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/Acp.d.ts.map +1 -1
- package/dist/Acp.js +195 -16
- package/dist/Acp.js.map +1 -1
- package/dist/AcpImage.test.d.ts +2 -0
- package/dist/AcpImage.test.d.ts.map +1 -0
- package/dist/AcpImage.test.js +470 -0
- package/dist/AcpImage.test.js.map +1 -0
- package/dist/Agent.d.ts +9 -2
- package/dist/Agent.d.ts.map +1 -1
- package/dist/Agent.js +43 -6
- package/dist/Agent.js.map +1 -1
- package/dist/AgentExecutor.d.ts +27 -5
- package/dist/AgentExecutor.d.ts.map +1 -1
- package/dist/AgentExecutor.js +29 -4
- package/dist/AgentExecutor.js.map +1 -1
- package/dist/AgentExecutorImage.test.d.ts +2 -0
- package/dist/AgentExecutorImage.test.d.ts.map +1 -0
- package/dist/AgentExecutorImage.test.js +63 -0
- package/dist/AgentExecutorImage.test.js.map +1 -0
- package/dist/AgentImage.test.d.ts +2 -0
- package/dist/AgentImage.test.d.ts.map +1 -0
- package/dist/AgentImage.test.js +176 -0
- package/dist/AgentImage.test.js.map +1 -0
- package/dist/AgentTools.d.ts +25 -5
- package/dist/AgentTools.d.ts.map +1 -1
- package/dist/AgentTools.js +40 -4
- package/dist/AgentTools.js.map +1 -1
- package/dist/AgentToolsImage.test.d.ts +2 -0
- package/dist/AgentToolsImage.test.d.ts.map +1 -0
- package/dist/AgentToolsImage.test.js +158 -0
- package/dist/AgentToolsImage.test.js.map +1 -0
- package/dist/Codex.js +6 -1
- package/dist/Codex.js.map +1 -1
- package/dist/Compaction.d.ts.map +1 -1
- package/dist/Compaction.js +16 -1
- package/dist/Compaction.js.map +1 -1
- package/dist/CompactionImage.test.d.ts +2 -0
- package/dist/CompactionImage.test.d.ts.map +1 -0
- package/dist/CompactionImage.test.js +105 -0
- package/dist/CompactionImage.test.js.map +1 -0
- package/dist/Copilot.d.ts.map +1 -1
- package/dist/Copilot.js +5 -1
- package/dist/Copilot.js.map +1 -1
- package/dist/Image.d.ts +192 -0
- package/dist/Image.d.ts.map +1 -0
- package/dist/Image.js +586 -0
- package/dist/Image.js.map +1 -0
- package/dist/Image.test.d.ts +2 -0
- package/dist/Image.test.d.ts.map +1 -0
- package/dist/Image.test.js +142 -0
- package/dist/Image.test.js.map +1 -0
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/fixtures/TestImages.d.ts +73 -0
- package/dist/fixtures/TestImages.d.ts.map +1 -0
- package/dist/fixtures/TestImages.js +263 -0
- package/dist/fixtures/TestImages.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/Acp.ts +274 -21
- package/src/AcpImage.test.ts +676 -0
- package/src/Agent.ts +69 -7
- package/src/AgentExecutor.ts +51 -3
- package/src/AgentExecutorImage.test.ts +103 -0
- package/src/AgentImage.test.ts +295 -0
- package/src/AgentTools.ts +78 -7
- package/src/AgentToolsImage.test.ts +302 -0
- package/src/Codex.ts +6 -1
- package/src/Compaction.ts +16 -1
- package/src/CompactionImage.test.ts +142 -0
- package/src/Copilot.ts +5 -1
- package/src/Image.test.ts +220 -0
- package/src/Image.ts +704 -0
- package/src/cli.ts +2 -1
- package/src/fixtures/TestImages.ts +287 -0
- package/src/index.ts +5 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import * as Photon from "@silvia-odwyer/photon-node"
|
|
2
|
+
import * as Zlib from "node:zlib"
|
|
3
|
+
import * as Encoding from "effect/Encoding"
|
|
4
|
+
|
|
5
|
+
export const encodePng = (options: {
|
|
6
|
+
readonly width: number
|
|
7
|
+
readonly height: number
|
|
8
|
+
readonly pixel?: (x: number, y: number) => readonly [number, number, number]
|
|
9
|
+
}): Uint8Array => {
|
|
10
|
+
const { width, height } = options
|
|
11
|
+
const pixels = new Uint8Array(width * height * 4)
|
|
12
|
+
for (let y = 0; y < height; y++) {
|
|
13
|
+
for (let x = 0; x < width; x++) {
|
|
14
|
+
pixels.set(
|
|
15
|
+
[...(options.pixel?.(x, y) ?? [200, 30, 30]), 255],
|
|
16
|
+
(y * width + x) * 4,
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const image = new Photon.PhotonImage(pixels, width, height)
|
|
21
|
+
try {
|
|
22
|
+
return image.get_bytes()
|
|
23
|
+
} finally {
|
|
24
|
+
image.free()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const noisePixel = (
|
|
29
|
+
x: number,
|
|
30
|
+
y: number,
|
|
31
|
+
): readonly [number, number, number] => {
|
|
32
|
+
let h = (x * 374761393 + y * 668265263) | 0
|
|
33
|
+
h = Math.imul(h ^ (h >>> 13), 1274126177)
|
|
34
|
+
h ^= h >>> 16
|
|
35
|
+
return [h & 0xff, (h >>> 8) & 0xff, (h >>> 16) & 0xff]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const tinyPng = encodePng({ width: 4, height: 3 })
|
|
39
|
+
|
|
40
|
+
const encodeOtherFormats = () => {
|
|
41
|
+
const image = Photon.PhotonImage.new_from_byteslice(tinyPng)
|
|
42
|
+
try {
|
|
43
|
+
return { jpeg: image.get_bytes_jpeg(85), webp: image.get_bytes_webp() }
|
|
44
|
+
} finally {
|
|
45
|
+
image.free()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const encoded = encodeOtherFormats()
|
|
50
|
+
export const tinyJpeg = encoded.jpeg
|
|
51
|
+
export const tinyWebp = encoded.webp
|
|
52
|
+
|
|
53
|
+
/** Two 4x2 frames (red, then blue), each displayed for 100ms, looping forever. */
|
|
54
|
+
export const animatedGif = new Uint8Array([
|
|
55
|
+
// GIF89a, logical screen, and a two-colour global palette.
|
|
56
|
+
71, 73, 70, 56, 57, 97, 4, 0, 2, 0, 128, 0, 0, 255, 0, 0, 0, 0, 255,
|
|
57
|
+
// NETSCAPE2.0 loop extension.
|
|
58
|
+
33, 255, 11, 78, 69, 84, 83, 67, 65, 80, 69, 50, 46, 48, 3, 1, 0, 0, 0,
|
|
59
|
+
// Frame 1: graphics control, image descriptor, and LZW pixels.
|
|
60
|
+
33, 249, 4, 0, 10, 0, 0, 0, 44, 0, 0, 0, 0, 4, 0, 2, 0, 0, 2, 7, 4, 65, 16, 4,
|
|
61
|
+
65, 16, 5, 0,
|
|
62
|
+
// Frame 2 uses the blue palette entry.
|
|
63
|
+
33, 249, 4, 0, 10, 0, 0, 0, 44, 0, 0, 0, 0, 4, 0, 2, 0, 0, 2, 7, 12, 195, 48,
|
|
64
|
+
12, 195, 48, 5, 0, 59,
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
export const supportedImages = [
|
|
68
|
+
{
|
|
69
|
+
fileName: "shot.png",
|
|
70
|
+
mediaType: "image/png",
|
|
71
|
+
data: tinyPng,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
fileName: "photo.jpeg",
|
|
75
|
+
mediaType: "image/jpeg",
|
|
76
|
+
data: tinyJpeg,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
fileName: "animated.gif",
|
|
80
|
+
mediaType: "image/gif",
|
|
81
|
+
data: animatedGif,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
fileName: "picture.webp",
|
|
85
|
+
mediaType: "image/webp",
|
|
86
|
+
data: tinyWebp,
|
|
87
|
+
},
|
|
88
|
+
] as const
|
|
89
|
+
|
|
90
|
+
const ascii = (text: string) => Array.from(text, (c) => c.charCodeAt(0))
|
|
91
|
+
const u16be = (n: number) => [(n >>> 8) & 0xff, n & 0xff]
|
|
92
|
+
const u16le = (n: number) => [n & 0xff, (n >>> 8) & 0xff]
|
|
93
|
+
const u24le = (n: number) => [n & 0xff, (n >>> 8) & 0xff, (n >>> 16) & 0xff]
|
|
94
|
+
const u32be = (n: number) => [
|
|
95
|
+
(n >>> 24) & 0xff,
|
|
96
|
+
(n >>> 16) & 0xff,
|
|
97
|
+
(n >>> 8) & 0xff,
|
|
98
|
+
n & 0xff,
|
|
99
|
+
]
|
|
100
|
+
const u32le = (n: number) => [
|
|
101
|
+
n & 0xff,
|
|
102
|
+
(n >>> 8) & 0xff,
|
|
103
|
+
(n >>> 16) & 0xff,
|
|
104
|
+
(n >>> 24) & 0xff,
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
const pngHeader = (width: number, height: number) => {
|
|
108
|
+
const ihdr = [
|
|
109
|
+
...ascii("IHDR"),
|
|
110
|
+
...u32be(width),
|
|
111
|
+
...u32be(height),
|
|
112
|
+
8,
|
|
113
|
+
6,
|
|
114
|
+
0,
|
|
115
|
+
0,
|
|
116
|
+
0,
|
|
117
|
+
]
|
|
118
|
+
return new Uint8Array([
|
|
119
|
+
0x89,
|
|
120
|
+
0x50,
|
|
121
|
+
0x4e,
|
|
122
|
+
0x47,
|
|
123
|
+
0x0d,
|
|
124
|
+
0x0a,
|
|
125
|
+
0x1a,
|
|
126
|
+
0x0a,
|
|
127
|
+
...u32be(13),
|
|
128
|
+
...ihdr,
|
|
129
|
+
...u32be(Zlib.crc32(new Uint8Array(ihdr))),
|
|
130
|
+
])
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const jpegHeader = (width: number, height: number) =>
|
|
134
|
+
new Uint8Array([
|
|
135
|
+
0xff,
|
|
136
|
+
0xd8,
|
|
137
|
+
// APP0 / JFIF, as emitted by most encoders, so the parser must skip it.
|
|
138
|
+
0xff,
|
|
139
|
+
0xe0,
|
|
140
|
+
...u16be(16),
|
|
141
|
+
...ascii("JFIF"),
|
|
142
|
+
0,
|
|
143
|
+
1,
|
|
144
|
+
2,
|
|
145
|
+
0,
|
|
146
|
+
0,
|
|
147
|
+
1,
|
|
148
|
+
0,
|
|
149
|
+
1,
|
|
150
|
+
0,
|
|
151
|
+
0,
|
|
152
|
+
// SOF0: length 17, 8-bit precision, height, width, 3 components.
|
|
153
|
+
0xff,
|
|
154
|
+
0xc0,
|
|
155
|
+
...u16be(17),
|
|
156
|
+
8,
|
|
157
|
+
...u16be(height),
|
|
158
|
+
...u16be(width),
|
|
159
|
+
3,
|
|
160
|
+
1,
|
|
161
|
+
0x22,
|
|
162
|
+
0,
|
|
163
|
+
2,
|
|
164
|
+
0x11,
|
|
165
|
+
1,
|
|
166
|
+
3,
|
|
167
|
+
0x11,
|
|
168
|
+
1,
|
|
169
|
+
])
|
|
170
|
+
|
|
171
|
+
const gifHeader = (width: number, height: number) =>
|
|
172
|
+
new Uint8Array([
|
|
173
|
+
...ascii("GIF89a"),
|
|
174
|
+
...u16le(width),
|
|
175
|
+
...u16le(height),
|
|
176
|
+
0,
|
|
177
|
+
0,
|
|
178
|
+
0,
|
|
179
|
+
])
|
|
180
|
+
|
|
181
|
+
/** Extended (VP8X) container, the only WebP form that carries a canvas size. */
|
|
182
|
+
const webpHeader = (width: number, height: number) =>
|
|
183
|
+
new Uint8Array([
|
|
184
|
+
...ascii("RIFF"),
|
|
185
|
+
...u32le(22),
|
|
186
|
+
...ascii("WEBP"),
|
|
187
|
+
...ascii("VP8X"),
|
|
188
|
+
...u32le(10),
|
|
189
|
+
0,
|
|
190
|
+
0,
|
|
191
|
+
0,
|
|
192
|
+
0,
|
|
193
|
+
...u24le(width - 1),
|
|
194
|
+
...u24le(height - 1),
|
|
195
|
+
])
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Header-only fixtures that declare canvases far over any sane pixel budget.
|
|
199
|
+
* They carry no pixel data, so nothing can decode them: a test that passes
|
|
200
|
+
* one to the decoder is a test that would decode a bomb.
|
|
201
|
+
*/
|
|
202
|
+
export const oversizedHeaders = [
|
|
203
|
+
{
|
|
204
|
+
mediaType: "image/png",
|
|
205
|
+
width: 16384,
|
|
206
|
+
height: 16384,
|
|
207
|
+
data: pngHeader(16384, 16384),
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
mediaType: "image/jpeg",
|
|
211
|
+
width: 65535,
|
|
212
|
+
height: 65535,
|
|
213
|
+
data: jpegHeader(65535, 65535),
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
mediaType: "image/gif",
|
|
217
|
+
width: 65535,
|
|
218
|
+
height: 65535,
|
|
219
|
+
data: gifHeader(65535, 65535),
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
mediaType: "image/webp",
|
|
223
|
+
width: 16384,
|
|
224
|
+
height: 16384,
|
|
225
|
+
data: webpHeader(16384, 16384),
|
|
226
|
+
},
|
|
227
|
+
] as const
|
|
228
|
+
|
|
229
|
+
/** Small container canvases hiding 8000x8000 frames, with no pixel payload. */
|
|
230
|
+
export const oversizedFrameHeaders = [
|
|
231
|
+
{
|
|
232
|
+
mediaType: "image/webp",
|
|
233
|
+
width: 8000,
|
|
234
|
+
height: 8000,
|
|
235
|
+
data: new Uint8Array([
|
|
236
|
+
...ascii("RIFF"),
|
|
237
|
+
...u32le(36),
|
|
238
|
+
...webpHeader(1, 1).subarray(8),
|
|
239
|
+
...ascii("VP8L"),
|
|
240
|
+
...u32le(5),
|
|
241
|
+
0x2f,
|
|
242
|
+
// VP8L packs width-1 and height-1 into 14 bits each.
|
|
243
|
+
...u32le(7999 | (7999 << 14)),
|
|
244
|
+
0, // RIFF chunk padding, not pixel data.
|
|
245
|
+
]),
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
mediaType: "image/gif",
|
|
249
|
+
width: 8000,
|
|
250
|
+
height: 8000,
|
|
251
|
+
data: new Uint8Array([
|
|
252
|
+
...gifHeader(1, 1),
|
|
253
|
+
0x2c, // Image descriptor.
|
|
254
|
+
...u16le(0),
|
|
255
|
+
...u16le(0),
|
|
256
|
+
...u16le(8000),
|
|
257
|
+
...u16le(8000),
|
|
258
|
+
0, // No local colour table.
|
|
259
|
+
2,
|
|
260
|
+
0, // LZW minimum code size and empty data sub-block.
|
|
261
|
+
0x3b, // Trailer.
|
|
262
|
+
]),
|
|
263
|
+
},
|
|
264
|
+
] as const
|
|
265
|
+
|
|
266
|
+
export const dimensions = (bytes: Uint8Array) => {
|
|
267
|
+
const image = Photon.PhotonImage.new_from_byteslice(bytes)
|
|
268
|
+
try {
|
|
269
|
+
return { width: image.get_width(), height: image.get_height() }
|
|
270
|
+
} finally {
|
|
271
|
+
image.free()
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export const bytesOf = (data: string | Uint8Array | URL): Uint8Array => {
|
|
276
|
+
if (data instanceof Uint8Array) return data
|
|
277
|
+
if (data instanceof URL) throw new Error(`Expected inline data, got ${data}`)
|
|
278
|
+
const base64 = data.startsWith("data:")
|
|
279
|
+
? data.slice(data.indexOf(",") + 1)
|
|
280
|
+
: data
|
|
281
|
+
const decoded = Encoding.decodeBase64(base64)
|
|
282
|
+
if (decoded._tag === "Failure") throw new Error("Invalid base64 image data")
|
|
283
|
+
return decoded.success
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export const equalBytes = (a: Uint8Array, b: Uint8Array): boolean =>
|
|
287
|
+
a.length === b.length && a.every((byte, i) => byte === b[i])
|