dexe-mcp 0.19.0 → 0.20.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/CHANGELOG.md +60 -0
- package/README.md +126 -228
- package/dexe-plugin/skills/dexe-create-dao/SKILL.md +3 -1
- package/dist/lib/avatarImage.d.ts +22 -0
- package/dist/lib/avatarImage.d.ts.map +1 -0
- package/dist/lib/avatarImage.js +199 -0
- package/dist/lib/avatarImage.js.map +1 -0
- package/dist/lib/imageSniff.d.ts +44 -0
- package/dist/lib/imageSniff.d.ts.map +1 -0
- package/dist/lib/imageSniff.js +145 -0
- package/dist/lib/imageSniff.js.map +1 -0
- package/dist/lib/qr.d.ts +9 -2
- package/dist/lib/qr.d.ts.map +1 -1
- package/dist/lib/qr.js +24 -12
- package/dist/lib/qr.js.map +1 -1
- package/dist/tools/daoCreate.d.ts.map +1 -1
- package/dist/tools/daoCreate.js +13 -3
- package/dist/tools/daoCreate.js.map +1 -1
- package/dist/tools/flow.d.ts +21 -5
- package/dist/tools/flow.d.ts.map +1 -1
- package/dist/tools/flow.js +40 -24
- package/dist/tools/flow.js.map +1 -1
- package/dist/tools/ipfs.d.ts +1 -0
- package/dist/tools/ipfs.d.ts.map +1 -1
- package/dist/tools/ipfs.js +49 -42
- package/dist/tools/ipfs.js.map +1 -1
- package/dist/tools/otc.d.ts.map +1 -1
- package/dist/tools/otc.js +26 -14
- package/dist/tools/otc.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic raster avatar renderer.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the old SVG identicon: the DeXe serving chain (Go ipfs-cache →
|
|
5
|
+
* R2 `<descCid>.jpeg` with hardcoded image/jpeg) only renders real raster
|
|
6
|
+
* bytes, so we rasterize the same design — 1–2 initials over a hash-coloured
|
|
7
|
+
* diagonal gradient — and encode an actual JPEG. Pure JS: RGBA buffer +
|
|
8
|
+
* embedded 8x8 bitmap font + jpeg-js. No canvas, no native deps.
|
|
9
|
+
*
|
|
10
|
+
* Same daoName → byte-identical output (all constants fixed, no randomness).
|
|
11
|
+
*/
|
|
12
|
+
import jpegJs from "jpeg-js";
|
|
13
|
+
/** djb2-style hash → unsigned 32-bit. Kept identical to the legacy SVG identicon so re-generated avatars keep their colours. */
|
|
14
|
+
export function hashString(s) {
|
|
15
|
+
let h = 5381;
|
|
16
|
+
for (let i = 0; i < s.length; i++)
|
|
17
|
+
h = ((h << 5) + h + s.charCodeAt(i)) >>> 0;
|
|
18
|
+
return h >>> 0;
|
|
19
|
+
}
|
|
20
|
+
/** First 1–2 alphanumeric characters of the DAO name, uppercased ("?" fallback). */
|
|
21
|
+
export function avatarInitials(daoName) {
|
|
22
|
+
const cleaned = daoName.replace(/[^\p{L}\p{N}]/gu, "");
|
|
23
|
+
return (cleaned.slice(0, 2) || "?").toUpperCase();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 8x8 bitmap font (font8x8_basic, public domain — Daniel Hepper).
|
|
27
|
+
* Row bytes top→bottom; bit n of a row = pixel at x=n (LSB is the LEFT column).
|
|
28
|
+
*/
|
|
29
|
+
const FONT_8X8 = {
|
|
30
|
+
"0": [0x3e, 0x63, 0x73, 0x7b, 0x6f, 0x67, 0x3e, 0x00],
|
|
31
|
+
"1": [0x0c, 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x3f, 0x00],
|
|
32
|
+
"2": [0x1e, 0x33, 0x30, 0x1c, 0x06, 0x33, 0x3f, 0x00],
|
|
33
|
+
"3": [0x1e, 0x33, 0x30, 0x1c, 0x30, 0x33, 0x1e, 0x00],
|
|
34
|
+
"4": [0x38, 0x3c, 0x36, 0x33, 0x7f, 0x30, 0x78, 0x00],
|
|
35
|
+
"5": [0x3f, 0x03, 0x1f, 0x30, 0x30, 0x33, 0x1e, 0x00],
|
|
36
|
+
"6": [0x1c, 0x06, 0x03, 0x1f, 0x33, 0x33, 0x1e, 0x00],
|
|
37
|
+
"7": [0x3f, 0x33, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x00],
|
|
38
|
+
"8": [0x1e, 0x33, 0x33, 0x1e, 0x33, 0x33, 0x1e, 0x00],
|
|
39
|
+
"9": [0x1e, 0x33, 0x33, 0x3e, 0x30, 0x18, 0x0e, 0x00],
|
|
40
|
+
A: [0x0c, 0x1e, 0x33, 0x33, 0x3f, 0x33, 0x33, 0x00],
|
|
41
|
+
B: [0x3f, 0x66, 0x66, 0x3e, 0x66, 0x66, 0x3f, 0x00],
|
|
42
|
+
C: [0x3c, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3c, 0x00],
|
|
43
|
+
D: [0x1f, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1f, 0x00],
|
|
44
|
+
E: [0x7f, 0x46, 0x16, 0x1e, 0x16, 0x46, 0x7f, 0x00],
|
|
45
|
+
F: [0x7f, 0x46, 0x16, 0x1e, 0x16, 0x06, 0x0f, 0x00],
|
|
46
|
+
G: [0x3c, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7c, 0x00],
|
|
47
|
+
H: [0x33, 0x33, 0x33, 0x3f, 0x33, 0x33, 0x33, 0x00],
|
|
48
|
+
I: [0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x00],
|
|
49
|
+
J: [0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1e, 0x00],
|
|
50
|
+
K: [0x67, 0x66, 0x36, 0x1e, 0x36, 0x66, 0x67, 0x00],
|
|
51
|
+
L: [0x0f, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7f, 0x00],
|
|
52
|
+
M: [0x63, 0x77, 0x7f, 0x7f, 0x6b, 0x63, 0x63, 0x00],
|
|
53
|
+
N: [0x63, 0x67, 0x6f, 0x7b, 0x73, 0x63, 0x63, 0x00],
|
|
54
|
+
O: [0x1c, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1c, 0x00],
|
|
55
|
+
P: [0x3f, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x0f, 0x00],
|
|
56
|
+
Q: [0x1e, 0x33, 0x33, 0x33, 0x3b, 0x1e, 0x38, 0x00],
|
|
57
|
+
R: [0x3f, 0x66, 0x66, 0x3e, 0x36, 0x66, 0x67, 0x00],
|
|
58
|
+
S: [0x1e, 0x33, 0x07, 0x0e, 0x38, 0x33, 0x1e, 0x00],
|
|
59
|
+
T: [0x3f, 0x2d, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x00],
|
|
60
|
+
U: [0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3f, 0x00],
|
|
61
|
+
V: [0x33, 0x33, 0x33, 0x33, 0x33, 0x1e, 0x0c, 0x00],
|
|
62
|
+
W: [0x63, 0x63, 0x63, 0x6b, 0x7f, 0x77, 0x63, 0x00],
|
|
63
|
+
X: [0x63, 0x63, 0x36, 0x1c, 0x1c, 0x36, 0x63, 0x00],
|
|
64
|
+
Y: [0x33, 0x33, 0x33, 0x1e, 0x0c, 0x0c, 0x1e, 0x00],
|
|
65
|
+
Z: [0x7f, 0x63, 0x31, 0x18, 0x4c, 0x66, 0x7f, 0x00],
|
|
66
|
+
"?": [0x1e, 0x33, 0x30, 0x18, 0x0c, 0x00, 0x0c, 0x00],
|
|
67
|
+
};
|
|
68
|
+
/** hsl (deg, 0–1, 0–1) → [r, g, b] 0–255. Matches CSS hsl() as used by the legacy SVG. */
|
|
69
|
+
function hslToRgb(h, s, l) {
|
|
70
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
71
|
+
const hp = ((h % 360) + 360) % 360 / 60;
|
|
72
|
+
const x = c * (1 - Math.abs((hp % 2) - 1));
|
|
73
|
+
let r = 0;
|
|
74
|
+
let g = 0;
|
|
75
|
+
let b = 0;
|
|
76
|
+
if (hp < 1)
|
|
77
|
+
[r, g, b] = [c, x, 0];
|
|
78
|
+
else if (hp < 2)
|
|
79
|
+
[r, g, b] = [x, c, 0];
|
|
80
|
+
else if (hp < 3)
|
|
81
|
+
[r, g, b] = [0, c, x];
|
|
82
|
+
else if (hp < 4)
|
|
83
|
+
[r, g, b] = [0, x, c];
|
|
84
|
+
else if (hp < 5)
|
|
85
|
+
[r, g, b] = [x, 0, c];
|
|
86
|
+
else
|
|
87
|
+
[r, g, b] = [c, 0, x];
|
|
88
|
+
const m = l - c / 2;
|
|
89
|
+
return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255)];
|
|
90
|
+
}
|
|
91
|
+
const GLYPH_GAP_COLS = 2; // font-pixel columns between two initials
|
|
92
|
+
const BLOCK_WIDTH_RATIO = 0.62; // initials block ≈ 62% of image width
|
|
93
|
+
const JPEG_QUALITY = 90;
|
|
94
|
+
const SUPERSAMPLE = 2;
|
|
95
|
+
/**
|
|
96
|
+
* Render the avatar and encode as JPEG. `size` is the output square in px.
|
|
97
|
+
* Renders at 2x and box-downscales, so glyph edges pick up light smoothing
|
|
98
|
+
* when the pixel scale is odd.
|
|
99
|
+
*/
|
|
100
|
+
export function renderAvatarJpeg(daoName, size = 512) {
|
|
101
|
+
const h = hashString(daoName);
|
|
102
|
+
const hue1 = h % 360;
|
|
103
|
+
const hue2 = (hue1 + 40 + ((h >>> 8) % 80)) % 360;
|
|
104
|
+
const c1 = hslToRgb(hue1, 0.7, 0.55);
|
|
105
|
+
const c2 = hslToRgb(hue2, 0.7, 0.35);
|
|
106
|
+
const big = size * SUPERSAMPLE;
|
|
107
|
+
const rgba = new Uint8Array(big * big * 4);
|
|
108
|
+
// Diagonal gradient, top-left c1 → bottom-right c2 (same axis as the old
|
|
109
|
+
// SVG linearGradient x1=0,y1=0 → x2=1,y2=1).
|
|
110
|
+
const denom = 2 * (big - 1) || 1;
|
|
111
|
+
for (let y = 0; y < big; y++) {
|
|
112
|
+
for (let x = 0; x < big; x++) {
|
|
113
|
+
const t = (x + y) / denom;
|
|
114
|
+
const i = (y * big + x) * 4;
|
|
115
|
+
rgba[i] = Math.round(c1[0] + (c2[0] - c1[0]) * t);
|
|
116
|
+
rgba[i + 1] = Math.round(c1[1] + (c2[1] - c1[1]) * t);
|
|
117
|
+
rgba[i + 2] = Math.round(c1[2] + (c2[2] - c1[2]) * t);
|
|
118
|
+
rgba[i + 3] = 255;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Initials, centered, white. font8x8 glyphs lean into the low columns, so
|
|
122
|
+
// crop each glyph to its used column range before laying out — otherwise
|
|
123
|
+
// the block sits visibly off-center.
|
|
124
|
+
const initials = avatarInitials(daoName);
|
|
125
|
+
const glyphs = [...initials].map((ch) => FONT_8X8[ch] ?? FONT_8X8["?"]);
|
|
126
|
+
const bounds = glyphs.map((glyph) => {
|
|
127
|
+
let mask = 0;
|
|
128
|
+
for (const row of glyph)
|
|
129
|
+
mask |= row;
|
|
130
|
+
if (mask === 0)
|
|
131
|
+
return { minCol: 0, width: 8 };
|
|
132
|
+
let minCol = 0;
|
|
133
|
+
while (((mask >> minCol) & 1) === 0)
|
|
134
|
+
minCol++;
|
|
135
|
+
let maxCol = 7;
|
|
136
|
+
while (((mask >> maxCol) & 1) === 0)
|
|
137
|
+
maxCol--;
|
|
138
|
+
return { minCol, width: maxCol - minCol + 1 };
|
|
139
|
+
});
|
|
140
|
+
const blockCols = bounds.reduce((sum, b) => sum + b.width, 0) + (glyphs.length - 1) * GLYPH_GAP_COLS;
|
|
141
|
+
// Clamp by height too: a single narrow glyph (e.g. "I", cropped width 4)
|
|
142
|
+
// would otherwise scale past the canvas vertically and clip at the top.
|
|
143
|
+
// Same ratio on both axes keeps margins consistent.
|
|
144
|
+
const scale = Math.max(1, Math.min(Math.floor((big * BLOCK_WIDTH_RATIO) / blockCols), Math.floor((big * BLOCK_WIDTH_RATIO) / 8)));
|
|
145
|
+
const blockW = blockCols * scale;
|
|
146
|
+
const blockH = 8 * scale;
|
|
147
|
+
const originX = (big - blockW) >> 1;
|
|
148
|
+
const originY = (big - blockH) >> 1;
|
|
149
|
+
let glyphX = originX;
|
|
150
|
+
glyphs.forEach((glyph, gi) => {
|
|
151
|
+
const { minCol, width } = bounds[gi];
|
|
152
|
+
for (let row = 0; row < 8; row++) {
|
|
153
|
+
const bits = glyph[row];
|
|
154
|
+
if (bits === 0)
|
|
155
|
+
continue;
|
|
156
|
+
for (let col = minCol; col < minCol + width; col++) {
|
|
157
|
+
if (((bits >> col) & 1) === 0)
|
|
158
|
+
continue;
|
|
159
|
+
const px = glyphX + (col - minCol) * scale;
|
|
160
|
+
const py = originY + row * scale;
|
|
161
|
+
for (let dy = 0; dy < scale; dy++) {
|
|
162
|
+
const rowBase = (py + dy) * big;
|
|
163
|
+
for (let dx = 0; dx < scale; dx++) {
|
|
164
|
+
const i = (rowBase + px + dx) * 4;
|
|
165
|
+
rgba[i] = 255;
|
|
166
|
+
rgba[i + 1] = 255;
|
|
167
|
+
rgba[i + 2] = 255;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
glyphX += (width + GLYPH_GAP_COLS) * scale;
|
|
173
|
+
});
|
|
174
|
+
// Box-downscale SUPERSAMPLE× → final size.
|
|
175
|
+
const out = Buffer.alloc(size * size * 4);
|
|
176
|
+
const n = SUPERSAMPLE * SUPERSAMPLE;
|
|
177
|
+
for (let y = 0; y < size; y++) {
|
|
178
|
+
for (let x = 0; x < size; x++) {
|
|
179
|
+
let r = 0;
|
|
180
|
+
let g = 0;
|
|
181
|
+
let b = 0;
|
|
182
|
+
for (let dy = 0; dy < SUPERSAMPLE; dy++) {
|
|
183
|
+
for (let dx = 0; dx < SUPERSAMPLE; dx++) {
|
|
184
|
+
const i = ((y * SUPERSAMPLE + dy) * big + x * SUPERSAMPLE + dx) * 4;
|
|
185
|
+
r += rgba[i];
|
|
186
|
+
g += rgba[i + 1];
|
|
187
|
+
b += rgba[i + 2];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const o = (y * size + x) * 4;
|
|
191
|
+
out[o] = Math.round(r / n);
|
|
192
|
+
out[o + 1] = Math.round(g / n);
|
|
193
|
+
out[o + 2] = Math.round(b / n);
|
|
194
|
+
out[o + 3] = 255;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return jpegJs.encode({ data: out, width: size, height: size }, JPEG_QUALITY).data;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=avatarImage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatarImage.js","sourceRoot":"","sources":["../../src/lib/avatarImage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,MAAM,MAAM,SAAS,CAAC;AAE7B,gIAAgI;AAChI,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,IAAI,CAAC,GAAG,IAAI,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9E,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;IACvD,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,QAAQ,GAAsC;IAClD,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACrD,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACrD,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACrD,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACrD,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACrD,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACrD,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACrD,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACrD,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACrD,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACrD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnD,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAA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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Magic-byte sniffing for avatar uploads.
|
|
3
|
+
*
|
|
4
|
+
* The DeXe serving chain is unforgiving about content type: the Go
|
|
5
|
+
* `ipfs-cache` service copies DAO avatar bytes to R2 under `<descCid>.jpeg`
|
|
6
|
+
* with a hardcoded `image/jpeg` content-type and no byte inspection, and the
|
|
7
|
+
* app.dexe.io `<img>` has no error fallback after a successful GET. Raster
|
|
8
|
+
* bytes (JPEG/PNG/WebP/GIF) survive that because browsers content-sniff
|
|
9
|
+
* rasters inside `<img>`; SVG does not — browsers never sniff SVG, so SVG
|
|
10
|
+
* bytes labeled `image/jpeg` render as a broken image. Hence: validate what
|
|
11
|
+
* the caller claims against what the bytes actually are.
|
|
12
|
+
*/
|
|
13
|
+
export type SniffedImageFormat = "jpeg" | "png" | "webp" | "gif" | "svg" | "html" | "unknown";
|
|
14
|
+
export type RasterFormat = Extract<SniffedImageFormat, "jpeg" | "png" | "webp" | "gif">;
|
|
15
|
+
/** Identify an image (or image impostor) by its magic bytes. */
|
|
16
|
+
export declare function sniffImageFormat(bytes: Uint8Array): SniffedImageFormat;
|
|
17
|
+
/**
|
|
18
|
+
* Gate for every avatar upload path. Returns the detected raster format and
|
|
19
|
+
* its true MIME type, or throws an actionable error for anything that would
|
|
20
|
+
* end up as a permanently broken avatar on app.dexe.io.
|
|
21
|
+
*/
|
|
22
|
+
export declare function assertRasterAvatar(bytes: Uint8Array): {
|
|
23
|
+
format: RasterFormat;
|
|
24
|
+
mime: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Best-effort remote validation for avatar CIDs passed **by reference**
|
|
28
|
+
* (dexe_ipfs_upload_dao_metadata / dexe_ipfs_update_dao_metadata /
|
|
29
|
+
* dexe_dao_create take an avatarCID that was pinned elsewhere, so the local
|
|
30
|
+
* byte gate never saw it). Fetches the first KB of `<cid>/<fileName>` off the
|
|
31
|
+
* gateway chain and sniffs it.
|
|
32
|
+
*
|
|
33
|
+
* Verdicts:
|
|
34
|
+
* - `{ ok: true }` — bytes fetched, real raster.
|
|
35
|
+
* - `{ ok: false, error }` — bytes fetched, NOT a raster → hard-block.
|
|
36
|
+
* - `{ ok: true, warning }` — unreachable/timeout (fresh pins often
|
|
37
|
+
* haven't propagated to gateways yet) → proceed, surface the warning.
|
|
38
|
+
*/
|
|
39
|
+
export declare function checkAvatarCidBytes(cid: string, fileName: string, gateways: string[], perRequestTimeoutMs?: number): Promise<{
|
|
40
|
+
ok: boolean;
|
|
41
|
+
error?: string;
|
|
42
|
+
warning?: string;
|
|
43
|
+
}>;
|
|
44
|
+
//# sourceMappingURL=imageSniff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageSniff.d.ts","sourceRoot":"","sources":["../../src/lib/imageSniff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;AAE9F,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,kBAAkB,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC;AAmBxF,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,kBAAkB,CAuBtE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA0B5F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAAE,EAClB,mBAAmB,SAAO,GACzB,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA2C5D"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Magic-byte sniffing for avatar uploads.
|
|
3
|
+
*
|
|
4
|
+
* The DeXe serving chain is unforgiving about content type: the Go
|
|
5
|
+
* `ipfs-cache` service copies DAO avatar bytes to R2 under `<descCid>.jpeg`
|
|
6
|
+
* with a hardcoded `image/jpeg` content-type and no byte inspection, and the
|
|
7
|
+
* app.dexe.io `<img>` has no error fallback after a successful GET. Raster
|
|
8
|
+
* bytes (JPEG/PNG/WebP/GIF) survive that because browsers content-sniff
|
|
9
|
+
* rasters inside `<img>`; SVG does not — browsers never sniff SVG, so SVG
|
|
10
|
+
* bytes labeled `image/jpeg` render as a broken image. Hence: validate what
|
|
11
|
+
* the caller claims against what the bytes actually are.
|
|
12
|
+
*/
|
|
13
|
+
const RASTER_MIME = {
|
|
14
|
+
jpeg: "image/jpeg",
|
|
15
|
+
png: "image/png",
|
|
16
|
+
webp: "image/webp",
|
|
17
|
+
gif: "image/gif",
|
|
18
|
+
};
|
|
19
|
+
const PNG_SIG = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
20
|
+
function startsWith(bytes, sig, offset = 0) {
|
|
21
|
+
if (bytes.length < offset + sig.length)
|
|
22
|
+
return false;
|
|
23
|
+
for (let i = 0; i < sig.length; i++) {
|
|
24
|
+
if (bytes[offset + i] !== sig[i])
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
/** Identify an image (or image impostor) by its magic bytes. */
|
|
30
|
+
export function sniffImageFormat(bytes) {
|
|
31
|
+
if (startsWith(bytes, [0xff, 0xd8, 0xff]))
|
|
32
|
+
return "jpeg";
|
|
33
|
+
if (startsWith(bytes, PNG_SIG))
|
|
34
|
+
return "png";
|
|
35
|
+
if (startsWith(bytes, [0x47, 0x49, 0x46, 0x38, 0x37, 0x61]) || startsWith(bytes, [0x47, 0x49, 0x46, 0x38, 0x39, 0x61])) {
|
|
36
|
+
return "gif";
|
|
37
|
+
}
|
|
38
|
+
// RIFF....WEBP
|
|
39
|
+
if (startsWith(bytes, [0x52, 0x49, 0x46, 0x46]) && startsWith(bytes, [0x57, 0x45, 0x42, 0x50], 8)) {
|
|
40
|
+
return "webp";
|
|
41
|
+
}
|
|
42
|
+
// Text-based impostors: skip BOM + leading whitespace (trimStart strips
|
|
43
|
+
// U+FEFF too — it's spec whitespace), look at the first tag.
|
|
44
|
+
const head = Buffer.from(bytes.slice(0, 512))
|
|
45
|
+
.toString("utf8")
|
|
46
|
+
.trimStart()
|
|
47
|
+
.toLowerCase();
|
|
48
|
+
if (head.startsWith("<?xml") || head.startsWith("<svg"))
|
|
49
|
+
return "svg";
|
|
50
|
+
if (head.startsWith("<!doctype")) {
|
|
51
|
+
// `<!DOCTYPE svg ...>` is a legal SVG prologue — don't call it HTML.
|
|
52
|
+
return /^<!doctype\s+svg/.test(head) ? "svg" : "html";
|
|
53
|
+
}
|
|
54
|
+
if (head.startsWith("<html"))
|
|
55
|
+
return "html";
|
|
56
|
+
return "unknown";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Gate for every avatar upload path. Returns the detected raster format and
|
|
60
|
+
* its true MIME type, or throws an actionable error for anything that would
|
|
61
|
+
* end up as a permanently broken avatar on app.dexe.io.
|
|
62
|
+
*/
|
|
63
|
+
export function assertRasterAvatar(bytes) {
|
|
64
|
+
const format = sniffImageFormat(bytes);
|
|
65
|
+
switch (format) {
|
|
66
|
+
case "jpeg":
|
|
67
|
+
case "png":
|
|
68
|
+
case "webp":
|
|
69
|
+
case "gif":
|
|
70
|
+
return { format, mime: RASTER_MIME[format] };
|
|
71
|
+
case "svg":
|
|
72
|
+
throw new Error("Avatar bytes are SVG, which never renders on app.dexe.io: the ipfs-cache service serves DAO avatars " +
|
|
73
|
+
"with a hardcoded image/jpeg content-type and browsers refuse SVG bytes labeled image/jpeg. " +
|
|
74
|
+
"Provide real raster bytes (JPEG/PNG/WebP/GIF), or use dexe_dao_generate_avatar to get a valid JPEG.");
|
|
75
|
+
case "html":
|
|
76
|
+
throw new Error("Avatar bytes look like an HTML page, not an image — most likely a gateway error/directory-listing page " +
|
|
77
|
+
"was downloaded instead of the image file. Re-fetch the image (use the full <cid>/<fileName> path, " +
|
|
78
|
+
"not the bare CID) and upload the actual raster bytes.");
|
|
79
|
+
default:
|
|
80
|
+
throw new Error("Avatar bytes are not a recognized raster image — expected JPEG (FF D8 FF), PNG, WebP, or GIF magic bytes. " +
|
|
81
|
+
"Check that the base64 payload is the image file itself (not a data: URL wrapper or a truncated buffer).");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Best-effort remote validation for avatar CIDs passed **by reference**
|
|
86
|
+
* (dexe_ipfs_upload_dao_metadata / dexe_ipfs_update_dao_metadata /
|
|
87
|
+
* dexe_dao_create take an avatarCID that was pinned elsewhere, so the local
|
|
88
|
+
* byte gate never saw it). Fetches the first KB of `<cid>/<fileName>` off the
|
|
89
|
+
* gateway chain and sniffs it.
|
|
90
|
+
*
|
|
91
|
+
* Verdicts:
|
|
92
|
+
* - `{ ok: true }` — bytes fetched, real raster.
|
|
93
|
+
* - `{ ok: false, error }` — bytes fetched, NOT a raster → hard-block.
|
|
94
|
+
* - `{ ok: true, warning }` — unreachable/timeout (fresh pins often
|
|
95
|
+
* haven't propagated to gateways yet) → proceed, surface the warning.
|
|
96
|
+
*/
|
|
97
|
+
export async function checkAvatarCidBytes(cid, fileName, gateways, perRequestTimeoutMs = 4000) {
|
|
98
|
+
const pinataGatewayToken = process.env.DEXE_PINATA_GATEWAY_TOKEN?.trim();
|
|
99
|
+
for (const gw of gateways.slice(0, 3)) {
|
|
100
|
+
const base = gw.replace(/\/+$/, "").replace(/\/ipfs$/, "");
|
|
101
|
+
const url = `${base}/ipfs/${cid}/${fileName}`;
|
|
102
|
+
const controller = new AbortController();
|
|
103
|
+
const t = setTimeout(() => controller.abort(), perRequestTimeoutMs);
|
|
104
|
+
try {
|
|
105
|
+
const headers = { Range: "bytes=0-1023" };
|
|
106
|
+
let host = null;
|
|
107
|
+
try {
|
|
108
|
+
host = new URL(base).hostname;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
host = null;
|
|
112
|
+
}
|
|
113
|
+
if (pinataGatewayToken && host && (host === "mypinata.cloud" || host.endsWith(".mypinata.cloud"))) {
|
|
114
|
+
headers["x-pinata-gateway-token"] = pinataGatewayToken;
|
|
115
|
+
}
|
|
116
|
+
const res = await fetch(url, { headers, signal: controller.signal });
|
|
117
|
+
if (!res.ok)
|
|
118
|
+
continue;
|
|
119
|
+
const head = new Uint8Array(await res.arrayBuffer());
|
|
120
|
+
try {
|
|
121
|
+
assertRasterAvatar(head);
|
|
122
|
+
return { ok: true };
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
return {
|
|
126
|
+
ok: false,
|
|
127
|
+
error: `avatarCID ${cid}/${fileName} does not contain a usable avatar: ${err instanceof Error ? err.message : String(err)}`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// gateway unreachable/timeout — try the next one
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
clearTimeout(t);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
ok: true,
|
|
140
|
+
warning: `avatar bytes at ${cid}/${fileName} were not reachable on any configured gateway (fresh pins can take a while ` +
|
|
141
|
+
`to propagate) — byte validation skipped. If this CID did not come from dexe_ipfs_upload_avatar or ` +
|
|
142
|
+
`dexe_dao_generate_avatar, verify it is a real raster image.`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=imageSniff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageSniff.js","sourceRoot":"","sources":["../../src/lib/imageSniff.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/qr.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export declare function renderQr(text: string): Promise<RenderedQr>;
|
|
|
20
20
|
/** External fallback image URL for clients that can render neither ASCII nor an image block. */
|
|
21
21
|
export declare function qrFallbackUrl(uri: string): string;
|
|
22
22
|
/** One MCP `content[]` item — text or image. Kept loose to match the tool call sites. */
|
|
23
|
-
type PairingContent = {
|
|
23
|
+
export type PairingContent = {
|
|
24
24
|
type: "text";
|
|
25
25
|
text: string;
|
|
26
26
|
} | {
|
|
@@ -28,6 +28,14 @@ type PairingContent = {
|
|
|
28
28
|
data: string;
|
|
29
29
|
mimeType: string;
|
|
30
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Just the scannable part of a pairing response: a captioned ASCII QR text
|
|
33
|
+
* block plus an `image/png` block. Composite write flows attach these to
|
|
34
|
+
* their no-signer responses so the QR renders inline exactly like
|
|
35
|
+
* `dexe_wc_connect` — the JSON envelope stays the caller's business.
|
|
36
|
+
* Empty array when QR rendering is unavailable.
|
|
37
|
+
*/
|
|
38
|
+
export declare function wcQrBlocks(uri: string): Promise<PairingContent[]>;
|
|
31
39
|
/**
|
|
32
40
|
* Build the MCP `content[]` for a WalletConnect pairing response, shared by
|
|
33
41
|
* `dexe_wc_connect` and `dexe_tx_send`'s auto-pairing branch so the QR + the
|
|
@@ -39,5 +47,4 @@ type PairingContent = {
|
|
|
39
47
|
* `renderHint` telling the assistant to echo the ASCII block verbatim.
|
|
40
48
|
*/
|
|
41
49
|
export declare function wcPairingContent(uri: string, chainId: number, extra?: Record<string, unknown>): Promise<PairingContent[]>;
|
|
42
|
-
export {};
|
|
43
50
|
//# sourceMappingURL=qr.d.ts.map
|
package/dist/lib/qr.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr.d.ts","sourceRoot":"","sources":["../../src/lib/qr.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iFAAiF;IACjF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAqBD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBhE;AAED,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,yFAAyF;AACzF,
|
|
1
|
+
{"version":3,"file":"qr.d.ts","sourceRoot":"","sources":["../../src/lib/qr.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iFAAiF;IACjF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAqBD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBhE;AAED,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,yFAAyF;AACzF,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAavE;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,cAAc,EAAE,CAAC,CAwB3B"}
|
package/dist/lib/qr.js
CHANGED
|
@@ -54,6 +54,27 @@ export async function renderQr(text) {
|
|
|
54
54
|
export function qrFallbackUrl(uri) {
|
|
55
55
|
return `https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=${encodeURIComponent(uri)}`;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Just the scannable part of a pairing response: a captioned ASCII QR text
|
|
59
|
+
* block plus an `image/png` block. Composite write flows attach these to
|
|
60
|
+
* their no-signer responses so the QR renders inline exactly like
|
|
61
|
+
* `dexe_wc_connect` — the JSON envelope stays the caller's business.
|
|
62
|
+
* Empty array when QR rendering is unavailable.
|
|
63
|
+
*/
|
|
64
|
+
export async function wcQrBlocks(uri) {
|
|
65
|
+
const { ascii, pngBase64 } = await renderQr(uri);
|
|
66
|
+
const blocks = [];
|
|
67
|
+
if (ascii) {
|
|
68
|
+
blocks.push({
|
|
69
|
+
type: "text",
|
|
70
|
+
text: `📱 Scan with your phone wallet (MetaMask / Trust / Rainbow):\n\n${ascii}`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (pngBase64) {
|
|
74
|
+
blocks.push({ type: "image", data: pngBase64, mimeType: "image/png" });
|
|
75
|
+
}
|
|
76
|
+
return blocks;
|
|
77
|
+
}
|
|
57
78
|
/**
|
|
58
79
|
* Build the MCP `content[]` for a WalletConnect pairing response, shared by
|
|
59
80
|
* `dexe_wc_connect` and `dexe_tx_send`'s auto-pairing branch so the QR + the
|
|
@@ -65,17 +86,8 @@ export function qrFallbackUrl(uri) {
|
|
|
65
86
|
* `renderHint` telling the assistant to echo the ASCII block verbatim.
|
|
66
87
|
*/
|
|
67
88
|
export async function wcPairingContent(uri, chainId, extra) {
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
if (ascii) {
|
|
71
|
-
content.push({
|
|
72
|
-
type: "text",
|
|
73
|
-
text: `📱 Scan with your phone wallet (MetaMask / Trust / Rainbow):\n\n${ascii}`,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
if (pngBase64) {
|
|
77
|
-
content.push({ type: "image", data: pngBase64, mimeType: "image/png" });
|
|
78
|
-
}
|
|
89
|
+
const content = await wcQrBlocks(uri);
|
|
90
|
+
const hasAscii = content.some((c) => c.type === "text");
|
|
79
91
|
content.push({
|
|
80
92
|
type: "text",
|
|
81
93
|
text: JSON.stringify({
|
|
@@ -83,7 +95,7 @@ export async function wcPairingContent(uri, chainId, extra) {
|
|
|
83
95
|
chainId,
|
|
84
96
|
uri,
|
|
85
97
|
qrFallbackUrl: qrFallbackUrl(uri),
|
|
86
|
-
renderHint:
|
|
98
|
+
renderHint: hasAscii
|
|
87
99
|
? "Echo the ASCII QR block above VERBATIM in a code block so the user can scan it. Do not summarize or redraw it."
|
|
88
100
|
: "QR rendering unavailable — show the user `qrFallbackUrl` to open a scannable image, or `uri` to paste into their wallet.",
|
|
89
101
|
next: "Approve the session on your phone, then poll dexe_wc_status until `connected` is true.",
|
package/dist/lib/qr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr.js","sourceRoot":"","sources":["../../src/lib/qr.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,IAAI,MAAqC,CAAC;AAE1C,KAAK,UAAU,UAAU;IACvB,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAA4B,CAAC;QAChE,yEAAyE;QACzE,8CAA8C;QAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,OAA8C,CAAC;QAC/D,MAAM,SAAS,GACb,CAAC,OAAQ,GAA8B,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAE,GAA6B,CAAC,CAAC,CAAC,SAAS,CAAC;YAC7G,CAAC,GAAG,IAAI,OAAQ,GAA8B,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAE,GAA6B,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACvH,MAAM,GAAG,SAAS,IAAI,IAAI,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,MAAM,EAAE,GAAG,MAAM,UAAU,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAEjD,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,IAAI,CAAC;QACH,kFAAkF;QAClF,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,GAAG,IAAI,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC;QACnF,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9B,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,OAAO,iEAAiE,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;AACpG,CAAC;AAOD
|
|
1
|
+
{"version":3,"file":"qr.js","sourceRoot":"","sources":["../../src/lib/qr.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daoCreate.d.ts","sourceRoot":"","sources":["../../src/tools/daoCreate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAMpE,OAAO,EAA0C,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"daoCreate.d.ts","sourceRoot":"","sources":["../../src/tools/daoCreate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAMpE,OAAO,EAA0C,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AA+CvD,KAAK,eAAe,GAAG,IAAI,CAAC,YAAY,EAAE,gBAAgB,GAAG,MAAM,CAAC,CAAC;AASrE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,QAAQ,GAAG,YAAY,CAAC;IACnC,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CAsDnF;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,eAAe,GAAG;IACtD,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CA6BA;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,aAAa,EACrB,EAAE,EAAE,oBAAoB,EACxB,KAAK,CAAC,EAAE,UAAU,GACjB,IAAI,CAwRN"}
|
package/dist/tools/daoCreate.js
CHANGED
|
@@ -5,10 +5,12 @@ import { PinataClient } from "../lib/ipfs.js";
|
|
|
5
5
|
import { markdownToSlate } from "../lib/markdownToSlate.js";
|
|
6
6
|
import { resolveChain } from "../config.js";
|
|
7
7
|
import { pinataUploadHint } from "../lib/requireEnv.js";
|
|
8
|
-
import { sendOrCollect } from "./flow.js";
|
|
8
|
+
import { attachPairingQr, sendOrCollect } from "./flow.js";
|
|
9
9
|
import { buildDeployGovPool, DeployParamsSchema } from "./daoDeploy.js";
|
|
10
10
|
import { checkDeployCap, checkUserKeeperAsset, checkTreasuryRemainder, checkLinearInitData, checkQuorumReachable, assertPreflight, } from "../lib/preflight.js";
|
|
11
11
|
import { quorumPctFromRaw } from "../lib/quorumRisk.js";
|
|
12
|
+
import { checkAvatarCidBytes } from "../lib/imageSniff.js";
|
|
13
|
+
import { resolveGateways } from "./ipfs.js";
|
|
12
14
|
/**
|
|
13
15
|
* `dexe_dao_create` — the one-call DAO deploy composite. Two ways to call it:
|
|
14
16
|
*
|
|
@@ -326,6 +328,14 @@ export function registerDaoCreateTools(server, ctx, signer, wc, state) {
|
|
|
326
328
|
documents: [],
|
|
327
329
|
};
|
|
328
330
|
if (input.avatarCID) {
|
|
331
|
+
// avatarCID arrives by reference — the upload tools validate their own
|
|
332
|
+
// bytes, but nothing forces the caller to have used them. Best-effort
|
|
333
|
+
// fetch + sniff; hard-block only on confirmed non-raster bytes (an SVG
|
|
334
|
+
// here becomes a permanently broken avatar on app.dexe.io).
|
|
335
|
+
const avatarCheck = await checkAvatarCidBytes(input.avatarCID, input.avatarFileName, resolveGateways(ctx));
|
|
336
|
+
if (!avatarCheck.ok) {
|
|
337
|
+
return err(avatarCheck.error ?? "avatarCID failed raster validation");
|
|
338
|
+
}
|
|
329
339
|
daoMeta.avatarCID = input.avatarCID;
|
|
330
340
|
daoMeta.avatarFileName = input.avatarFileName;
|
|
331
341
|
daoMeta.avatarUrl = `https://${input.avatarCID}.ipfs.dweb.link/${input.avatarFileName}`;
|
|
@@ -373,7 +383,7 @@ export function registerDaoCreateTools(server, ctx, signer, wc, state) {
|
|
|
373
383
|
/* ignore */
|
|
374
384
|
}
|
|
375
385
|
}
|
|
376
|
-
return ok({
|
|
386
|
+
return attachPairingQr(ok({
|
|
377
387
|
mode: result.mode,
|
|
378
388
|
daoName: input.daoName,
|
|
379
389
|
chainId,
|
|
@@ -385,7 +395,7 @@ export function registerDaoCreateTools(server, ctx, signer, wc, state) {
|
|
|
385
395
|
steps: result.steps,
|
|
386
396
|
...(result.enableWrites ? { enableWrites: result.enableWrites } : {}),
|
|
387
397
|
...(result.pairing ? { pairing: result.pairing } : {}),
|
|
388
|
-
});
|
|
398
|
+
}), result.pairingContent);
|
|
389
399
|
});
|
|
390
400
|
}
|
|
391
401
|
//# sourceMappingURL=daoCreate.js.map
|