solfaces 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +518 -0
- package/dist/agent/index.cjs +51 -0
- package/dist/agent/index.cjs.map +1 -0
- package/dist/agent/index.d.cts +65 -0
- package/dist/agent/index.d.ts +65 -0
- package/dist/agent/index.js +6 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/mcp-server.cjs +836 -0
- package/dist/chunk-2DIKGLXZ.cjs +126 -0
- package/dist/chunk-2DIKGLXZ.cjs.map +1 -0
- package/dist/chunk-A6N3RPEA.cjs +111 -0
- package/dist/chunk-A6N3RPEA.cjs.map +1 -0
- package/dist/chunk-CVFO7YHY.cjs +97 -0
- package/dist/chunk-CVFO7YHY.cjs.map +1 -0
- package/dist/chunk-H3SK3MNX.cjs +409 -0
- package/dist/chunk-H3SK3MNX.cjs.map +1 -0
- package/dist/chunk-KSGFMW33.js +401 -0
- package/dist/chunk-KSGFMW33.js.map +1 -0
- package/dist/chunk-LQWJRHGC.js +86 -0
- package/dist/chunk-LQWJRHGC.js.map +1 -0
- package/dist/chunk-RX6D5FGH.js +211 -0
- package/dist/chunk-RX6D5FGH.js.map +1 -0
- package/dist/chunk-SNJABBAT.js +107 -0
- package/dist/chunk-SNJABBAT.js.map +1 -0
- package/dist/chunk-VMNATBH3.cjs +222 -0
- package/dist/chunk-VMNATBH3.cjs.map +1 -0
- package/dist/chunk-WURY4QGH.js +117 -0
- package/dist/chunk-WURY4QGH.js.map +1 -0
- package/dist/core/index.cjs +82 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +104 -0
- package/dist/core/index.d.ts +104 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.cjs +100 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.cjs +543 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +28 -0
- package/dist/react/index.d.ts +28 -0
- package/dist/react/index.js +541 -0
- package/dist/react/index.js.map +1 -0
- package/dist/solfaces.cdn.global.js +3 -0
- package/dist/solfaces.cdn.global.js.map +1 -0
- package/dist/themes/index.cjs +48 -0
- package/dist/themes/index.cjs.map +1 -0
- package/dist/themes/index.d.cts +14 -0
- package/dist/themes/index.d.ts +14 -0
- package/dist/themes/index.js +3 -0
- package/dist/themes/index.js.map +1 -0
- package/dist/traits-DAFZnXeS.d.cts +61 -0
- package/dist/traits-DAFZnXeS.d.ts +61 -0
- package/dist/vanilla/index.cjs +43 -0
- package/dist/vanilla/index.cjs.map +1 -0
- package/dist/vanilla/index.d.cts +7 -0
- package/dist/vanilla/index.d.ts +7 -0
- package/dist/vanilla/index.js +39 -0
- package/dist/vanilla/index.js.map +1 -0
- package/package.json +100 -0
- package/python/solfaces.py +475 -0
- package/skill.md +463 -0
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
// src/core/traits.ts
|
|
6
|
+
var SKIN_COLORS = [
|
|
7
|
+
"#ffd5b0",
|
|
8
|
+
"#f4c794",
|
|
9
|
+
"#e0a370",
|
|
10
|
+
"#c68642",
|
|
11
|
+
"#8d5524",
|
|
12
|
+
"#4a2c17"
|
|
13
|
+
];
|
|
14
|
+
var EYE_COLORS = [
|
|
15
|
+
"#3d2b1f",
|
|
16
|
+
// dark brown
|
|
17
|
+
"#4a80c4",
|
|
18
|
+
// blue
|
|
19
|
+
"#5a9a5a",
|
|
20
|
+
// green
|
|
21
|
+
"#c89430",
|
|
22
|
+
// amber
|
|
23
|
+
"#8a8a8a"
|
|
24
|
+
// gray
|
|
25
|
+
];
|
|
26
|
+
var HAIR_COLORS = [
|
|
27
|
+
"#1a1a1a",
|
|
28
|
+
// black
|
|
29
|
+
"#6b3a2a",
|
|
30
|
+
// brown
|
|
31
|
+
"#d4a844",
|
|
32
|
+
// blonde
|
|
33
|
+
"#c44a20",
|
|
34
|
+
// ginger
|
|
35
|
+
"#c8e64a",
|
|
36
|
+
// neon lime
|
|
37
|
+
"#6090e0",
|
|
38
|
+
// neon blue
|
|
39
|
+
"#14F195",
|
|
40
|
+
// neon mint
|
|
41
|
+
"#e040c0"
|
|
42
|
+
// neon magenta
|
|
43
|
+
];
|
|
44
|
+
var BG_COLORS = [
|
|
45
|
+
"#c8e64a",
|
|
46
|
+
"#6090e0",
|
|
47
|
+
"#14F195",
|
|
48
|
+
"#e8dcc8",
|
|
49
|
+
"#f85149"
|
|
50
|
+
];
|
|
51
|
+
function djb2(str) {
|
|
52
|
+
let hash = 5381;
|
|
53
|
+
for (let i = 0; i < str.length; i++) {
|
|
54
|
+
hash = (hash << 5) + hash + str.charCodeAt(i) | 0;
|
|
55
|
+
}
|
|
56
|
+
return hash >>> 0;
|
|
57
|
+
}
|
|
58
|
+
function mulberry32(seed) {
|
|
59
|
+
let s = seed | 0;
|
|
60
|
+
return () => {
|
|
61
|
+
s = s + 1831565813 | 0;
|
|
62
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
63
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
64
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function generateTraits(walletAddress, overrides) {
|
|
68
|
+
const seed = djb2(walletAddress);
|
|
69
|
+
const rand = mulberry32(seed);
|
|
70
|
+
const traits = {
|
|
71
|
+
faceShape: Math.floor(rand() * 4),
|
|
72
|
+
skinColor: Math.floor(rand() * 6),
|
|
73
|
+
eyeStyle: Math.floor(rand() * 8),
|
|
74
|
+
eyeColor: Math.floor(rand() * 5),
|
|
75
|
+
eyebrows: Math.floor(rand() * 5),
|
|
76
|
+
nose: Math.floor(rand() * 4),
|
|
77
|
+
mouth: Math.floor(rand() * 6),
|
|
78
|
+
hairStyle: Math.floor(rand() * 8),
|
|
79
|
+
hairColor: Math.floor(rand() * 8),
|
|
80
|
+
accessory: Math.floor(rand() * 6),
|
|
81
|
+
bgColor: Math.floor(rand() * 5)
|
|
82
|
+
};
|
|
83
|
+
return overrides ? { ...traits, ...overrides } : traits;
|
|
84
|
+
}
|
|
85
|
+
var FACE_LABELS = ["Round", "Square", "Oval", "Hexagon"];
|
|
86
|
+
var SKIN_LABELS = ["Light Peach", "Warm Tan", "Golden Brown", "Medium Brown", "Deep Brown", "Rich Dark Brown"];
|
|
87
|
+
var EYE_STYLE_LABELS = ["Round", "Dots", "Almond", "Wide", "Sleepy", "Winking", "Lashes", "Narrow"];
|
|
88
|
+
var EYE_COLOR_LABELS = ["Dark Brown", "Blue", "Green", "Amber", "Gray"];
|
|
89
|
+
var BROW_LABELS = ["None", "Thin", "Thick", "Arched", "Angled"];
|
|
90
|
+
var NOSE_LABELS = ["None", "Dot", "Triangle", "Button"];
|
|
91
|
+
var MOUTH_LABELS = ["Smile", "Neutral", "Grin", "Open", "Smirk", "Wide Smile"];
|
|
92
|
+
var HAIR_STYLE_LABELS = ["Bald", "Short", "Spiky", "Swept", "Mohawk", "Long", "Bob", "Buzz"];
|
|
93
|
+
var HAIR_COLOR_LABELS = ["Black", "Brown", "Blonde", "Ginger", "Neon Lime", "Neon Blue", "Solana Mint", "Neon Magenta"];
|
|
94
|
+
var ACCESSORY_LABELS = ["None", "None", "Round Glasses", "Square Glasses", "Earring", "Bandana"];
|
|
95
|
+
var BG_COLOR_LABELS = ["Lime", "Blue", "Mint", "Sand", "Red"];
|
|
96
|
+
function getTraitLabels(traits) {
|
|
97
|
+
return {
|
|
98
|
+
faceShape: FACE_LABELS[traits.faceShape] ?? "Round",
|
|
99
|
+
skinColor: SKIN_LABELS[traits.skinColor] ?? "Warm Tan",
|
|
100
|
+
eyeStyle: EYE_STYLE_LABELS[traits.eyeStyle] ?? "Round",
|
|
101
|
+
eyeColor: EYE_COLOR_LABELS[traits.eyeColor] ?? "Dark Brown",
|
|
102
|
+
eyebrows: BROW_LABELS[traits.eyebrows] ?? "None",
|
|
103
|
+
nose: NOSE_LABELS[traits.nose] ?? "None",
|
|
104
|
+
mouth: MOUTH_LABELS[traits.mouth] ?? "Smile",
|
|
105
|
+
hairStyle: HAIR_STYLE_LABELS[traits.hairStyle] ?? "Bald",
|
|
106
|
+
hairColor: HAIR_COLOR_LABELS[traits.hairColor] ?? "Black",
|
|
107
|
+
accessory: ACCESSORY_LABELS[traits.accessory] ?? "None",
|
|
108
|
+
bgColor: BG_COLOR_LABELS[traits.bgColor] ?? "Lime"
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function traitHash(walletAddress) {
|
|
112
|
+
return (djb2(walletAddress) >>> 0).toString(16).padStart(8, "0");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/core/renderer.ts
|
|
116
|
+
function renderFace(t, skin) {
|
|
117
|
+
if (t.faceShape === 0) return `<circle cx="32" cy="34" r="20" fill="${skin}"/>`;
|
|
118
|
+
if (t.faceShape === 1) return `<rect x="12" y="14" width="40" height="40" rx="8" ry="8" fill="${skin}"/>`;
|
|
119
|
+
if (t.faceShape === 2) return `<ellipse cx="32" cy="34" rx="18" ry="22" fill="${skin}"/>`;
|
|
120
|
+
if (t.faceShape === 3) return `<path d="M32 12 L50 24 L50 44 L32 56 L14 44 L14 24 Z" fill="${skin}" stroke-linejoin="round"/>`;
|
|
121
|
+
return `<circle cx="32" cy="34" r="20" fill="${skin}"/>`;
|
|
122
|
+
}
|
|
123
|
+
function renderEyes(t, c, w = "white") {
|
|
124
|
+
const l = 24, r = 40, y = 30;
|
|
125
|
+
switch (t.eyeStyle) {
|
|
126
|
+
case 0:
|
|
127
|
+
return `<circle cx="${l}" cy="${y}" r="3.5" fill="${w}"/><circle cx="${l + 1}" cy="${y}" r="2" fill="${c}"/><circle cx="${r}" cy="${y}" r="3.5" fill="${w}"/><circle cx="${r + 1}" cy="${y}" r="2" fill="${c}"/>`;
|
|
128
|
+
case 1:
|
|
129
|
+
return `<circle cx="${l}" cy="${y}" r="2" fill="${c}"/><circle cx="${r}" cy="${y}" r="2" fill="${c}"/>`;
|
|
130
|
+
case 2:
|
|
131
|
+
return `<ellipse cx="${l}" cy="${y}" rx="4" ry="2.5" fill="${w}"/><circle cx="${l + 0.5}" cy="${y}" r="1.5" fill="${c}"/><ellipse cx="${r}" cy="${y}" rx="4" ry="2.5" fill="${w}"/><circle cx="${r + 0.5}" cy="${y}" r="1.5" fill="${c}"/>`;
|
|
132
|
+
case 3:
|
|
133
|
+
return `<circle cx="${l}" cy="${y}" r="4.5" fill="${w}"/><circle cx="${l}" cy="${y + 0.5}" r="2.5" fill="${c}"/><circle cx="${r}" cy="${y}" r="4.5" fill="${w}"/><circle cx="${r}" cy="${y + 0.5}" r="2.5" fill="${c}"/>`;
|
|
134
|
+
case 4:
|
|
135
|
+
return `<ellipse cx="${l}" cy="${y + 1}" rx="3.5" ry="2" fill="${w}"/><circle cx="${l}" cy="${y + 1}" r="1.5" fill="${c}"/><line x1="${l - 4}" y1="${y - 0.5}" x2="${l + 4}" y2="${y - 0.5}" stroke="${c}" stroke-width="1" stroke-linecap="round"/><ellipse cx="${r}" cy="${y + 1}" rx="3.5" ry="2" fill="${w}"/><circle cx="${r}" cy="${y + 1}" r="1.5" fill="${c}"/><line x1="${r - 4}" y1="${y - 0.5}" x2="${r + 4}" y2="${y - 0.5}" stroke="${c}" stroke-width="1" stroke-linecap="round"/>`;
|
|
136
|
+
case 5:
|
|
137
|
+
return `<path d="M${l - 3} ${y} Q${l} ${y + 3} ${l + 3} ${y}" fill="none" stroke="${c}" stroke-width="1.5" stroke-linecap="round"/><circle cx="${r}" cy="${y}" r="3.5" fill="${w}"/><circle cx="${r + 1}" cy="${y}" r="2" fill="${c}"/>`;
|
|
138
|
+
case 6:
|
|
139
|
+
return `<circle cx="${l}" cy="${y}" r="3" fill="${w}"/><circle cx="${l + 0.5}" cy="${y}" r="1.5" fill="${c}"/><line x1="${l + 2}" y1="${y - 3}" x2="${l + 3.5}" y2="${y - 4.5}" stroke="${c}" stroke-width="0.8" stroke-linecap="round"/><line x1="${l + 3}" y1="${y - 2}" x2="${l + 4.5}" y2="${y - 3}" stroke="${c}" stroke-width="0.8" stroke-linecap="round"/><circle cx="${r}" cy="${y}" r="3" fill="${w}"/><circle cx="${r + 0.5}" cy="${y}" r="1.5" fill="${c}"/><line x1="${r + 2}" y1="${y - 3}" x2="${r + 3.5}" y2="${y - 4.5}" stroke="${c}" stroke-width="0.8" stroke-linecap="round"/><line x1="${r + 3}" y1="${y - 2}" x2="${r + 4.5}" y2="${y - 3}" stroke="${c}" stroke-width="0.8" stroke-linecap="round"/>`;
|
|
140
|
+
case 7:
|
|
141
|
+
return `<ellipse cx="${l}" cy="${y}" rx="4" ry="1.2" fill="${w}"/><ellipse cx="${l + 0.5}" cy="${y}" rx="2" ry="1" fill="${c}"/><ellipse cx="${r}" cy="${y}" rx="4" ry="1.2" fill="${w}"/><ellipse cx="${r + 0.5}" cy="${y}" rx="2" ry="1" fill="${c}"/>`;
|
|
142
|
+
default:
|
|
143
|
+
return `<circle cx="${l}" cy="${y}" r="3" fill="${w}"/><circle cx="${l + 1}" cy="${y}" r="2" fill="${c}"/><circle cx="${r}" cy="${y}" r="3" fill="${w}"/><circle cx="${r + 1}" cy="${y}" r="2" fill="${c}"/>`;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function renderEyebrows(t, col = "#2a2020") {
|
|
147
|
+
const l = 24, r = 40, y = 25;
|
|
148
|
+
switch (t.eyebrows) {
|
|
149
|
+
case 0:
|
|
150
|
+
return "";
|
|
151
|
+
case 1:
|
|
152
|
+
return `<line x1="${l - 3}" y1="${y}" x2="${l + 3}" y2="${y}" stroke="${col}" stroke-width="0.8" stroke-linecap="round"/><line x1="${r - 3}" y1="${y}" x2="${r + 3}" y2="${y}" stroke="${col}" stroke-width="0.8" stroke-linecap="round"/>`;
|
|
153
|
+
case 2:
|
|
154
|
+
return `<line x1="${l - 3.5}" y1="${y}" x2="${l + 3.5}" y2="${y}" stroke="${col}" stroke-width="2" stroke-linecap="round"/><line x1="${r - 3.5}" y1="${y}" x2="${r + 3.5}" y2="${y}" stroke="${col}" stroke-width="2" stroke-linecap="round"/>`;
|
|
155
|
+
case 3:
|
|
156
|
+
return `<path d="M${l - 3.5} ${y + 1} Q${l} ${y - 2} ${l + 3.5} ${y + 1}" fill="none" stroke="${col}" stroke-width="1" stroke-linecap="round"/><path d="M${r - 3.5} ${y + 1} Q${r} ${y - 2} ${r + 3.5} ${y + 1}" fill="none" stroke="${col}" stroke-width="1" stroke-linecap="round"/>`;
|
|
157
|
+
case 4:
|
|
158
|
+
return `<line x1="${l - 3}" y1="${y - 1}" x2="${l + 3}" y2="${y + 1}" stroke="${col}" stroke-width="1.2" stroke-linecap="round"/><line x1="${r - 3}" y1="${y + 1}" x2="${r + 3}" y2="${y - 1}" stroke="${col}" stroke-width="1.2" stroke-linecap="round"/>`;
|
|
159
|
+
default:
|
|
160
|
+
return "";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function renderNose(t, skin, noseCol) {
|
|
164
|
+
const cx = 32, y = 36;
|
|
165
|
+
const sh = noseCol ?? skin + "aa";
|
|
166
|
+
switch (t.nose) {
|
|
167
|
+
case 0:
|
|
168
|
+
return "";
|
|
169
|
+
case 1:
|
|
170
|
+
return `<circle cx="${cx}" cy="${y}" r="1.5" fill="${sh}"/>`;
|
|
171
|
+
case 2:
|
|
172
|
+
return `<path d="M${cx} ${y - 1.5} L${cx + 2.5} ${y + 2} L${cx - 2.5} ${y + 2} Z" fill="${sh}"/>`;
|
|
173
|
+
case 3:
|
|
174
|
+
return `<circle cx="${cx - 1.5}" cy="${y}" r="1" fill="${sh}"/><circle cx="${cx + 1.5}" cy="${y}" r="1" fill="${sh}"/>`;
|
|
175
|
+
default:
|
|
176
|
+
return "";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function renderMouth(t, col = "#c05050", teethCol = "white") {
|
|
180
|
+
const cx = 32, y = 42;
|
|
181
|
+
switch (t.mouth) {
|
|
182
|
+
case 0:
|
|
183
|
+
return `<path d="M${cx - 4} ${y} Q${cx} ${y + 4} ${cx + 4} ${y}" fill="none" stroke="${col}" stroke-width="1.2" stroke-linecap="round"/>`;
|
|
184
|
+
case 1:
|
|
185
|
+
return `<line x1="${cx - 3}" y1="${y + 1}" x2="${cx + 3}" y2="${y + 1}" stroke="${col}" stroke-width="1.2" stroke-linecap="round"/>`;
|
|
186
|
+
case 2:
|
|
187
|
+
return `<path d="M${cx - 6} ${y} Q${cx} ${y + 5} ${cx + 6} ${y}" fill="none" stroke="${col}" stroke-width="1.5" stroke-linecap="round"/>`;
|
|
188
|
+
case 3:
|
|
189
|
+
return `<ellipse cx="${cx}" cy="${y + 1}" rx="3" ry="2.5" fill="${col}" opacity="0.8"/>`;
|
|
190
|
+
case 4:
|
|
191
|
+
return `<path d="M${cx - 4} ${y + 1} Q${cx - 1} ${y + 1} ${cx + 4} ${y - 1}" fill="none" stroke="${col}" stroke-width="1.2" stroke-linecap="round"/>`;
|
|
192
|
+
case 5:
|
|
193
|
+
return `<path d="M${cx - 6} ${y} Q${cx} ${y + 6} ${cx + 6} ${y}" fill="${teethCol}" stroke="${col}" stroke-width="1"/>`;
|
|
194
|
+
default:
|
|
195
|
+
return `<path d="M${cx - 4} ${y} Q${cx} ${y + 4} ${cx + 4} ${y}" fill="none" stroke="${col}" stroke-width="1.2" stroke-linecap="round"/>`;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function renderHair(t, col) {
|
|
199
|
+
switch (t.hairStyle) {
|
|
200
|
+
case 0:
|
|
201
|
+
return "";
|
|
202
|
+
// Bald
|
|
203
|
+
case 1:
|
|
204
|
+
return `<rect x="14" y="12" width="36" height="12" rx="6" ry="6" fill="${col}"/>`;
|
|
205
|
+
case 2:
|
|
206
|
+
return `<g fill="${col}"><rect x="14" y="16" width="36" height="8" rx="2"/><polygon points="18,16 22,6 26,16"/><polygon points="26,16 30,4 34,16"/><polygon points="34,16 38,6 42,16"/><polygon points="42,16 46,10 48,16"/></g>`;
|
|
207
|
+
case 3:
|
|
208
|
+
return `<g fill="${col}"><rect x="14" y="14" width="36" height="10" rx="4"/><path d="M14 18 Q8 14 10 8 Q14 10 20 14 Z"/></g>`;
|
|
209
|
+
case 4:
|
|
210
|
+
return `<rect x="26" y="4" width="12" height="20" rx="4" ry="2" fill="${col}"/>`;
|
|
211
|
+
case 5:
|
|
212
|
+
return `<g fill="${col}"><rect x="14" y="12" width="36" height="10" rx="4"/><rect x="10" y="18" width="8" height="24" rx="3"/><rect x="46" y="18" width="8" height="24" rx="3"/></g>`;
|
|
213
|
+
case 6:
|
|
214
|
+
return `<path d="M12 22 Q12 10 32 10 Q52 10 52 22 L52 38 Q52 42 48 42 L48 26 Q48 16 32 16 Q16 16 16 26 L16 42 Q12 42 12 38 Z" fill="${col}"/>`;
|
|
215
|
+
case 7:
|
|
216
|
+
return `<rect x="15" y="13" width="34" height="9" rx="8" ry="4" fill="${col}" opacity="0.7"/>`;
|
|
217
|
+
default:
|
|
218
|
+
return "";
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function renderAccessory(t, col = "#444") {
|
|
222
|
+
if (t.accessory <= 1) return "";
|
|
223
|
+
if (t.accessory === 2) return `<g fill="none" stroke="${col}" stroke-width="1"><circle cx="24" cy="30" r="5"/><circle cx="40" cy="30" r="5"/><line x1="29" y1="30" x2="35" y2="30"/><line x1="19" y1="30" x2="14" y2="28"/><line x1="45" y1="30" x2="50" y2="28"/></g>`;
|
|
224
|
+
if (t.accessory === 3) return `<g fill="none" stroke="${col}" stroke-width="1"><rect x="19" y="26" width="10" height="8" rx="1"/><rect x="35" y="26" width="10" height="8" rx="1"/><line x1="29" y1="30" x2="35" y2="30"/><line x1="19" y1="30" x2="14" y2="28"/><line x1="45" y1="30" x2="50" y2="28"/></g>`;
|
|
225
|
+
if (t.accessory === 4) return `<circle cx="11" cy="36" r="2" fill="${col}" stroke="${col}" stroke-width="0.5"/>`;
|
|
226
|
+
if (t.accessory === 5) return `<g><rect x="12" y="20" width="40" height="4" rx="1" fill="${col}"/><path d="M12 22 L8 26 L12 24 Z" fill="${col}"/></g>`;
|
|
227
|
+
return "";
|
|
228
|
+
}
|
|
229
|
+
function renderSolFaceSVG(walletAddress, options) {
|
|
230
|
+
const { size = 64, theme, traitOverrides, enableBlink, className, colorOverrides } = options ?? {};
|
|
231
|
+
const traits = generateTraits(walletAddress, traitOverrides);
|
|
232
|
+
const skinColors = theme?.skinColors ?? SKIN_COLORS;
|
|
233
|
+
const eyeColors = theme?.eyeColors ?? EYE_COLORS;
|
|
234
|
+
const hairColors = theme?.hairColors ?? HAIR_COLORS;
|
|
235
|
+
const bgColors = theme?.bgColors ?? BG_COLORS;
|
|
236
|
+
const skin = colorOverrides?.skin ?? skinColors[traits.skinColor % skinColors.length];
|
|
237
|
+
const eyeCol = colorOverrides?.eyes ?? eyeColors[traits.eyeColor % eyeColors.length];
|
|
238
|
+
const hairCol = colorOverrides?.hair ?? hairColors[traits.hairColor % hairColors.length];
|
|
239
|
+
const bgCol = colorOverrides?.bg ?? bgColors[traits.bgColor % bgColors.length];
|
|
240
|
+
const bgOpacity = theme?.bgOpacity ?? 0.15;
|
|
241
|
+
const bgRadius = theme?.bgRadius ?? 4;
|
|
242
|
+
const mouthCol = colorOverrides?.mouth ?? theme?.mouthColor ?? "#c05050";
|
|
243
|
+
const browCol = colorOverrides?.eyebrow ?? theme?.eyebrowColor ?? "#2a2020";
|
|
244
|
+
const accCol = colorOverrides?.accessory ?? theme?.accessoryColor ?? "#444";
|
|
245
|
+
const eyeWhite = colorOverrides?.eyeWhite ?? theme?.eyeWhiteColor ?? "white";
|
|
246
|
+
const noseCol = colorOverrides?.nose ?? theme?.noseColor;
|
|
247
|
+
const classAttr = className ? ` class="${className}"` : "";
|
|
248
|
+
let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="${size}" height="${size}"${classAttr}>`;
|
|
249
|
+
svg += `<rect x="0" y="0" width="64" height="64" fill="${bgCol}" opacity="${bgOpacity}" rx="${bgRadius}"/>`;
|
|
250
|
+
const blinkEnabled = !!enableBlink;
|
|
251
|
+
const blinkDuration = typeof enableBlink === "object" ? enableBlink.duration ?? 4 : 4;
|
|
252
|
+
const blinkDelay = typeof enableBlink === "object" ? enableBlink.delay ?? 0 : 0;
|
|
253
|
+
if (blinkEnabled) {
|
|
254
|
+
const uid = `sf-${walletAddress.slice(0, 8)}`;
|
|
255
|
+
const delayStr = blinkDelay ? ` ${blinkDelay}s` : "";
|
|
256
|
+
svg += `<style>@keyframes ${uid}-blink{0%,90%,100%{transform:scaleY(1)}95%{transform:scaleY(0.1)}}.${uid}-eyes{animation:${uid}-blink ${blinkDuration}s ease-in-out${delayStr} infinite;transform-origin:32px 30px}</style>`;
|
|
257
|
+
}
|
|
258
|
+
svg += renderHair(traits, hairCol);
|
|
259
|
+
svg += renderFace(traits, skin);
|
|
260
|
+
if (blinkEnabled) {
|
|
261
|
+
const uid = `sf-${walletAddress.slice(0, 8)}`;
|
|
262
|
+
svg += `<g class="${uid}-eyes">`;
|
|
263
|
+
}
|
|
264
|
+
svg += renderEyes(traits, eyeCol, eyeWhite);
|
|
265
|
+
if (blinkEnabled) svg += `</g>`;
|
|
266
|
+
svg += renderEyebrows(traits, browCol);
|
|
267
|
+
svg += renderNose(traits, skin, noseCol);
|
|
268
|
+
svg += renderMouth(traits, mouthCol, eyeWhite);
|
|
269
|
+
svg += renderAccessory(traits, accCol);
|
|
270
|
+
if (theme?.border) {
|
|
271
|
+
svg += `<rect x="0" y="0" width="64" height="64" fill="none" stroke="${theme.border.color}" stroke-width="${theme.border.width}" rx="${bgRadius}"/>`;
|
|
272
|
+
}
|
|
273
|
+
svg += `</svg>`;
|
|
274
|
+
return svg;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/core/describe.ts
|
|
278
|
+
var FACE_SHAPES = {
|
|
279
|
+
0: "round",
|
|
280
|
+
1: "square with softly rounded corners",
|
|
281
|
+
2: "oval",
|
|
282
|
+
3: "angular, hexagonal"
|
|
283
|
+
};
|
|
284
|
+
var SKIN_TONES = {
|
|
285
|
+
0: "light peach",
|
|
286
|
+
1: "warm tan",
|
|
287
|
+
2: "golden brown",
|
|
288
|
+
3: "medium brown",
|
|
289
|
+
4: "deep brown",
|
|
290
|
+
5: "rich dark brown"
|
|
291
|
+
};
|
|
292
|
+
var EYE_STYLES = {
|
|
293
|
+
0: "round, wide-open",
|
|
294
|
+
1: "small and dot-like",
|
|
295
|
+
2: "almond-shaped",
|
|
296
|
+
3: "wide and expressive",
|
|
297
|
+
4: "sleepy, half-lidded",
|
|
298
|
+
5: "playfully winking",
|
|
299
|
+
6: "adorned with lashes",
|
|
300
|
+
7: "narrow and observant"
|
|
301
|
+
};
|
|
302
|
+
var EYE_COLORS_DESC = {
|
|
303
|
+
0: "dark brown",
|
|
304
|
+
1: "blue",
|
|
305
|
+
2: "green",
|
|
306
|
+
3: "amber",
|
|
307
|
+
4: "gray"
|
|
308
|
+
};
|
|
309
|
+
var EYEBROW_STYLES = {
|
|
310
|
+
0: "",
|
|
311
|
+
// none — omitted from description
|
|
312
|
+
1: "thin",
|
|
313
|
+
2: "thick, prominent",
|
|
314
|
+
3: "elegantly arched",
|
|
315
|
+
4: "sharply angled"
|
|
316
|
+
};
|
|
317
|
+
var NOSE_STYLES = {
|
|
318
|
+
0: "",
|
|
319
|
+
// none
|
|
320
|
+
1: "a small dot nose",
|
|
321
|
+
2: "a triangular nose",
|
|
322
|
+
3: "a button nose with visible nostrils"
|
|
323
|
+
};
|
|
324
|
+
var MOUTH_STYLES = {
|
|
325
|
+
0: "a gentle smile",
|
|
326
|
+
1: "a neutral, straight expression",
|
|
327
|
+
2: "a wide grin",
|
|
328
|
+
3: "a small, open mouth",
|
|
329
|
+
4: "a confident smirk",
|
|
330
|
+
5: "a broad, toothy smile"
|
|
331
|
+
};
|
|
332
|
+
var HAIR_STYLES = {
|
|
333
|
+
0: "bald, with no hair",
|
|
334
|
+
1: "short, neatly cropped hair",
|
|
335
|
+
2: "tall, spiky hair",
|
|
336
|
+
3: "side-swept hair",
|
|
337
|
+
4: "a bold mohawk",
|
|
338
|
+
5: "long hair that falls past the shoulders",
|
|
339
|
+
6: "a clean bob cut",
|
|
340
|
+
7: "a close buzz cut"
|
|
341
|
+
};
|
|
342
|
+
var HAIR_COLORS_DESC = {
|
|
343
|
+
0: "jet black",
|
|
344
|
+
1: "brown",
|
|
345
|
+
2: "blonde",
|
|
346
|
+
3: "ginger red",
|
|
347
|
+
4: "neon lime green",
|
|
348
|
+
5: "neon blue",
|
|
349
|
+
6: "Solana mint green",
|
|
350
|
+
7: "neon magenta"
|
|
351
|
+
};
|
|
352
|
+
var ACCESSORY_DESC = {
|
|
353
|
+
0: "",
|
|
354
|
+
1: "",
|
|
355
|
+
2: "round glasses",
|
|
356
|
+
3: "square-framed glasses",
|
|
357
|
+
4: "a gold earring",
|
|
358
|
+
5: "a red bandana"
|
|
359
|
+
};
|
|
360
|
+
var BG_COLORS_DESC = {
|
|
361
|
+
0: "lime green",
|
|
362
|
+
1: "blue",
|
|
363
|
+
2: "Solana mint green",
|
|
364
|
+
3: "warm sand",
|
|
365
|
+
4: "red"
|
|
366
|
+
};
|
|
367
|
+
function describeAppearance(walletAddress, options) {
|
|
368
|
+
const traits = generateTraits(walletAddress);
|
|
369
|
+
const {
|
|
370
|
+
includeBackground = true,
|
|
371
|
+
format = "paragraph",
|
|
372
|
+
perspective = "third",
|
|
373
|
+
name
|
|
374
|
+
} = options ?? {};
|
|
375
|
+
if (format === "structured") {
|
|
376
|
+
return buildStructured(traits, includeBackground);
|
|
377
|
+
}
|
|
378
|
+
if (format === "compact") {
|
|
379
|
+
return buildCompact(traits);
|
|
380
|
+
}
|
|
381
|
+
return buildParagraph(traits, perspective, name, includeBackground);
|
|
382
|
+
}
|
|
383
|
+
function buildParagraph(t, perspective, name, includeBg) {
|
|
384
|
+
const parts = [];
|
|
385
|
+
const subject = perspective === "first" ? name ? `I'm ${name}. I have` : "I have" : name ? `${name} has` : "This SolFace has";
|
|
386
|
+
const have = perspective === "first" ? "have" : "has";
|
|
387
|
+
const my = perspective === "first" ? "my" : "their";
|
|
388
|
+
const im = perspective === "first" ? "I'm" : "They're";
|
|
389
|
+
parts.push(`${subject} a ${FACE_SHAPES[t.faceShape] ?? "round"} face with ${SKIN_TONES[t.skinColor] ?? "warm"} skin`);
|
|
390
|
+
const eyeStyle = EYE_STYLES[t.eyeStyle] ?? "round";
|
|
391
|
+
const eyeColor = EYE_COLORS_DESC[t.eyeColor] ?? "dark";
|
|
392
|
+
parts.push(`${eyeStyle} ${eyeColor} eyes`);
|
|
393
|
+
const brows = EYEBROW_STYLES[t.eyebrows];
|
|
394
|
+
if (brows) {
|
|
395
|
+
parts.push(`${brows} eyebrows`);
|
|
396
|
+
}
|
|
397
|
+
const hairStyle = HAIR_STYLES[t.hairStyle] ?? "";
|
|
398
|
+
const hairColor = HAIR_COLORS_DESC[t.hairColor] ?? "";
|
|
399
|
+
if (t.hairStyle === 0) {
|
|
400
|
+
parts.push("and is bald");
|
|
401
|
+
} else if (hairStyle.startsWith("a ")) {
|
|
402
|
+
parts.push(`and a ${hairColor} ${hairStyle.slice(2)}`);
|
|
403
|
+
} else {
|
|
404
|
+
parts.push(`and ${hairColor} ${hairStyle}`);
|
|
405
|
+
}
|
|
406
|
+
let desc = parts[0];
|
|
407
|
+
if (parts.length > 2) {
|
|
408
|
+
desc += ", " + parts.slice(1, -1).join(", ") + ", " + parts[parts.length - 1];
|
|
409
|
+
} else if (parts.length === 2) {
|
|
410
|
+
desc += " and " + parts[1];
|
|
411
|
+
}
|
|
412
|
+
desc += ".";
|
|
413
|
+
const nose = NOSE_STYLES[t.nose];
|
|
414
|
+
if (nose) {
|
|
415
|
+
const noseSubject = perspective === "first" ? "I have" : (name ?? "They") + (name ? " has" : " have");
|
|
416
|
+
desc += ` ${noseSubject} ${nose}.`;
|
|
417
|
+
}
|
|
418
|
+
const acc = ACCESSORY_DESC[t.accessory];
|
|
419
|
+
if (acc) {
|
|
420
|
+
desc += ` ${im} wearing ${acc}.`;
|
|
421
|
+
}
|
|
422
|
+
const mouth = MOUTH_STYLES[t.mouth] ?? "a smile";
|
|
423
|
+
const mouthVerb = perspective === "first" ? "I have" : (name ?? "They") + (name ? " has" : " have");
|
|
424
|
+
desc += ` ${mouthVerb} ${mouth}.`;
|
|
425
|
+
if (includeBg) {
|
|
426
|
+
const bg = BG_COLORS_DESC[t.bgColor] ?? "colorful";
|
|
427
|
+
desc += ` The background is ${bg}.`;
|
|
428
|
+
}
|
|
429
|
+
return desc;
|
|
430
|
+
}
|
|
431
|
+
function buildStructured(t, includeBg) {
|
|
432
|
+
const lines = [
|
|
433
|
+
`Face: ${FACE_SHAPES[t.faceShape] ?? "round"}`,
|
|
434
|
+
`Skin: ${SKIN_TONES[t.skinColor] ?? "warm"}`,
|
|
435
|
+
`Eyes: ${EYE_STYLES[t.eyeStyle] ?? "round"}, ${EYE_COLORS_DESC[t.eyeColor] ?? "dark"}`
|
|
436
|
+
];
|
|
437
|
+
const brows = EYEBROW_STYLES[t.eyebrows];
|
|
438
|
+
if (brows) lines.push(`Eyebrows: ${brows}`);
|
|
439
|
+
const nose = NOSE_STYLES[t.nose];
|
|
440
|
+
if (nose) lines.push(`Nose: ${nose.replace(/^a /, "")}`);
|
|
441
|
+
lines.push(`Mouth: ${MOUTH_STYLES[t.mouth] ?? "smile"}`);
|
|
442
|
+
if (t.hairStyle === 0) {
|
|
443
|
+
lines.push("Hair: bald");
|
|
444
|
+
} else {
|
|
445
|
+
const hs = HAIR_STYLES[t.hairStyle] ?? "";
|
|
446
|
+
const hc = HAIR_COLORS_DESC[t.hairColor] ?? "";
|
|
447
|
+
lines.push(`Hair: ${hc} ${hs.startsWith("a ") ? hs.slice(2) : hs}`);
|
|
448
|
+
}
|
|
449
|
+
const acc = ACCESSORY_DESC[t.accessory];
|
|
450
|
+
if (acc) lines.push(`Accessory: ${acc}`);
|
|
451
|
+
if (includeBg) {
|
|
452
|
+
lines.push(`Background: ${BG_COLORS_DESC[t.bgColor] ?? "colorful"}`);
|
|
453
|
+
}
|
|
454
|
+
return lines.join("\n");
|
|
455
|
+
}
|
|
456
|
+
function buildCompact(t) {
|
|
457
|
+
const parts = [];
|
|
458
|
+
parts.push(`${FACE_SHAPES[t.faceShape] ?? "round"} face`);
|
|
459
|
+
parts.push(`${SKIN_TONES[t.skinColor] ?? "warm"} skin`);
|
|
460
|
+
parts.push(`${EYE_COLORS_DESC[t.eyeColor] ?? "dark"} ${EYE_STYLES[t.eyeStyle] ?? "round"} eyes`);
|
|
461
|
+
if (t.hairStyle === 0) {
|
|
462
|
+
parts.push("bald");
|
|
463
|
+
} else {
|
|
464
|
+
const hs = (HAIR_STYLES[t.hairStyle] ?? "hair").replace(/,.*/, "");
|
|
465
|
+
const hc = HAIR_COLORS_DESC[t.hairColor] ?? "";
|
|
466
|
+
parts.push(`${hc} ${hs.startsWith("a ") ? hs.slice(2) : hs}`);
|
|
467
|
+
}
|
|
468
|
+
const acc = ACCESSORY_DESC[t.accessory];
|
|
469
|
+
if (acc) parts.push(acc);
|
|
470
|
+
parts.push((MOUTH_STYLES[t.mouth] ?? "smiling").replace(/^a /, ""));
|
|
471
|
+
return parts.join(", ");
|
|
472
|
+
}
|
|
473
|
+
function agentAppearancePrompt(walletAddress, agentName) {
|
|
474
|
+
const desc = describeAppearance(walletAddress, {
|
|
475
|
+
perspective: "first",
|
|
476
|
+
name: agentName,
|
|
477
|
+
includeBackground: false,
|
|
478
|
+
format: "paragraph"
|
|
479
|
+
});
|
|
480
|
+
const hash = (djb2Simple(walletAddress) >>> 0).toString(16).padStart(8, "0");
|
|
481
|
+
return `My visual identity is a SolFace avatar (ID: ${hash}) derived from my wallet address. ${desc} This appearance is deterministic \u2014 anyone who looks up my wallet will see the same face.`;
|
|
482
|
+
}
|
|
483
|
+
function djb2Simple(str) {
|
|
484
|
+
let hash = 5381;
|
|
485
|
+
for (let i = 0; i < str.length; i++) {
|
|
486
|
+
hash = (hash << 5) + hash + str.charCodeAt(i) | 0;
|
|
487
|
+
}
|
|
488
|
+
return hash >>> 0;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/themes/presets.ts
|
|
492
|
+
var solanaTheme = {
|
|
493
|
+
bgColors: ["#14F195", "#9945FF", "#00C2FF", "#FFD700", "#FF6B6B"],
|
|
494
|
+
bgOpacity: 0.2,
|
|
495
|
+
bgRadius: 4
|
|
496
|
+
};
|
|
497
|
+
var darkTheme = {
|
|
498
|
+
bgColors: ["#1a1b23", "#2d1b69", "#0a2463", "#1b2838", "#2a0a3a"],
|
|
499
|
+
mouthColor: "#e06070",
|
|
500
|
+
eyebrowColor: "#aaa",
|
|
501
|
+
accessoryColor: "#888",
|
|
502
|
+
eyeWhiteColor: "#e0e0e0",
|
|
503
|
+
bgOpacity: 1,
|
|
504
|
+
bgRadius: 4
|
|
505
|
+
};
|
|
506
|
+
var lightTheme = {
|
|
507
|
+
bgColors: ["#f0f4ff", "#fff0f5", "#f0fff0", "#fffff0", "#f5f0ff"],
|
|
508
|
+
bgOpacity: 1,
|
|
509
|
+
bgRadius: 8
|
|
510
|
+
};
|
|
511
|
+
var monoTheme = {
|
|
512
|
+
skinColors: ["#e0e0e0", "#c0c0c0", "#a0a0a0", "#808080", "#606060", "#404040"],
|
|
513
|
+
eyeColors: ["#333", "#555", "#777", "#999", "#bbb"],
|
|
514
|
+
hairColors: ["#1a1a1a", "#333", "#555", "#777", "#999", "#bbb", "#ddd", "#eee"],
|
|
515
|
+
bgColors: ["#f0f0f0", "#e0e0e0", "#d0d0d0", "#c0c0c0", "#b0b0b0"],
|
|
516
|
+
mouthColor: "#666",
|
|
517
|
+
eyebrowColor: "#555",
|
|
518
|
+
accessoryColor: "#777",
|
|
519
|
+
eyeWhiteColor: "#f0f0f0",
|
|
520
|
+
bgOpacity: 0.3,
|
|
521
|
+
bgRadius: 4
|
|
522
|
+
};
|
|
523
|
+
var neonTheme = {
|
|
524
|
+
bgColors: ["#0d0d0d", "#1a0a2e", "#0a1628", "#0d1a0d", "#1a0a0a"],
|
|
525
|
+
hairColors: ["#ff00ff", "#00ffff", "#ff6600", "#39ff14", "#ff3366", "#6600ff", "#ffff00", "#00ff99"],
|
|
526
|
+
eyeColors: ["#ff00ff", "#00ffff", "#39ff14", "#ff6600", "#ffff00"],
|
|
527
|
+
mouthColor: "#ff3366",
|
|
528
|
+
eyebrowColor: "#ccc",
|
|
529
|
+
accessoryColor: "#00ffff",
|
|
530
|
+
eyeWhiteColor: "#1a1a1a",
|
|
531
|
+
bgOpacity: 1,
|
|
532
|
+
bgRadius: 4,
|
|
533
|
+
border: { color: "#39ff14", width: 1 }
|
|
534
|
+
};
|
|
535
|
+
var jupiterTheme = {
|
|
536
|
+
bgColors: ["#131b2e", "#1b2540", "#0f1926", "#1e2d4a", "#162033"],
|
|
537
|
+
mouthColor: "#c7d4e8",
|
|
538
|
+
eyebrowColor: "#8899aa",
|
|
539
|
+
accessoryColor: "#6882a0",
|
|
540
|
+
eyeWhiteColor: "#d0d8e8",
|
|
541
|
+
bgOpacity: 1,
|
|
542
|
+
bgRadius: 6,
|
|
543
|
+
border: { color: "#3a5a8c", width: 0.5 }
|
|
544
|
+
};
|
|
545
|
+
var phantomTheme = {
|
|
546
|
+
bgColors: ["#1c0e30", "#2a1548", "#1e0f36", "#251240", "#20103a"],
|
|
547
|
+
mouthColor: "#d4a0e8",
|
|
548
|
+
eyebrowColor: "#9966cc",
|
|
549
|
+
accessoryColor: "#ab8dd6",
|
|
550
|
+
eyeWhiteColor: "#d8c8e8",
|
|
551
|
+
bgOpacity: 1,
|
|
552
|
+
bgRadius: 6,
|
|
553
|
+
border: { color: "#ab8dd6", width: 0.5 }
|
|
554
|
+
};
|
|
555
|
+
var circleTheme = {
|
|
556
|
+
bgRadius: 999
|
|
557
|
+
};
|
|
558
|
+
var PRESET_THEMES = {
|
|
559
|
+
solana: solanaTheme,
|
|
560
|
+
dark: darkTheme,
|
|
561
|
+
light: lightTheme,
|
|
562
|
+
mono: monoTheme,
|
|
563
|
+
neon: neonTheme,
|
|
564
|
+
jupiter: jupiterTheme,
|
|
565
|
+
phantom: phantomTheme,
|
|
566
|
+
circle: circleTheme
|
|
567
|
+
};
|
|
568
|
+
function getPresetTheme(name, overrides) {
|
|
569
|
+
const base = PRESET_THEMES[name];
|
|
570
|
+
if (!base) return overrides ?? {};
|
|
571
|
+
return overrides ? { ...base, ...overrides } : base;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/agent/tools.ts
|
|
575
|
+
var generateSolfaceSvg = {
|
|
576
|
+
name: "generate_solface_svg",
|
|
577
|
+
description: "Generate a deterministic SVG avatar for a Solana wallet address. Returns an SVG string that can be embedded in HTML, saved as a file, or converted to a data URI. The same wallet always produces the same face.",
|
|
578
|
+
parameters: {
|
|
579
|
+
type: "object",
|
|
580
|
+
properties: {
|
|
581
|
+
wallet: {
|
|
582
|
+
type: "string",
|
|
583
|
+
description: "Solana wallet address (base58 public key)"
|
|
584
|
+
},
|
|
585
|
+
size: {
|
|
586
|
+
type: "number",
|
|
587
|
+
description: "SVG width/height in pixels. Default: 64"
|
|
588
|
+
},
|
|
589
|
+
theme: {
|
|
590
|
+
type: "string",
|
|
591
|
+
description: "Preset theme name: solana, dark, light, mono, neon, jupiter, phantom, circle",
|
|
592
|
+
enum: ["solana", "dark", "light", "mono", "neon", "jupiter", "phantom", "circle"]
|
|
593
|
+
},
|
|
594
|
+
enableBlink: {
|
|
595
|
+
type: "boolean",
|
|
596
|
+
description: "Enable CSS blink animation on the eyes. Default: false"
|
|
597
|
+
}
|
|
598
|
+
},
|
|
599
|
+
required: ["wallet"]
|
|
600
|
+
},
|
|
601
|
+
handler(params) {
|
|
602
|
+
const wallet = params.wallet;
|
|
603
|
+
const size = params.size ?? 64;
|
|
604
|
+
const enableBlink = params.enableBlink ?? false;
|
|
605
|
+
const themeName = params.theme;
|
|
606
|
+
const theme = themeName ? getPresetTheme(themeName) : void 0;
|
|
607
|
+
return renderSolFaceSVG(wallet, { size, theme, enableBlink });
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
var describeSolface = {
|
|
611
|
+
name: "describe_solface",
|
|
612
|
+
description: "Generate a natural language description of a wallet's SolFace avatar. Useful for alt text, profile bios, system prompts, and accessibility.",
|
|
613
|
+
parameters: {
|
|
614
|
+
type: "object",
|
|
615
|
+
properties: {
|
|
616
|
+
wallet: {
|
|
617
|
+
type: "string",
|
|
618
|
+
description: "Solana wallet address (base58 public key)"
|
|
619
|
+
},
|
|
620
|
+
format: {
|
|
621
|
+
type: "string",
|
|
622
|
+
description: "Output format: paragraph (flowing text), structured (labeled lines), compact (short comma-separated). Default: paragraph",
|
|
623
|
+
enum: ["paragraph", "structured", "compact"]
|
|
624
|
+
},
|
|
625
|
+
perspective: {
|
|
626
|
+
type: "string",
|
|
627
|
+
description: 'Narrative perspective: "first" for self-description ("I have..."), "third" for external ("This SolFace has..."). Default: third',
|
|
628
|
+
enum: ["first", "third"]
|
|
629
|
+
},
|
|
630
|
+
name: {
|
|
631
|
+
type: "string",
|
|
632
|
+
description: 'Optional name to use instead of "This SolFace" or "I". E.g. "Atlas"'
|
|
633
|
+
}
|
|
634
|
+
},
|
|
635
|
+
required: ["wallet"]
|
|
636
|
+
},
|
|
637
|
+
handler(params) {
|
|
638
|
+
return describeAppearance(params.wallet, {
|
|
639
|
+
format: params.format ?? "paragraph",
|
|
640
|
+
perspective: params.perspective ?? "third",
|
|
641
|
+
name: params.name
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
var getSolfaceTraits = {
|
|
646
|
+
name: "get_solface_traits",
|
|
647
|
+
description: "Get the raw numeric trait values, human-readable labels, and deterministic hash for a wallet's SolFace avatar. Returns structured data useful for programmatic decisions about appearance.",
|
|
648
|
+
parameters: {
|
|
649
|
+
type: "object",
|
|
650
|
+
properties: {
|
|
651
|
+
wallet: {
|
|
652
|
+
type: "string",
|
|
653
|
+
description: "Solana wallet address (base58 public key)"
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
required: ["wallet"]
|
|
657
|
+
},
|
|
658
|
+
handler(params) {
|
|
659
|
+
const wallet = params.wallet;
|
|
660
|
+
const traits = generateTraits(wallet);
|
|
661
|
+
const labels = getTraitLabels(traits);
|
|
662
|
+
const hash = traitHash(wallet);
|
|
663
|
+
return { traits, labels, hash };
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
var getAgentIdentity = {
|
|
667
|
+
name: "get_agent_identity",
|
|
668
|
+
description: "Generate a system prompt snippet that gives an AI agent a visual identity based on its Solana wallet. The snippet describes the agent's appearance in first person and explains the deterministic nature of SolFace avatars.",
|
|
669
|
+
parameters: {
|
|
670
|
+
type: "object",
|
|
671
|
+
properties: {
|
|
672
|
+
wallet: {
|
|
673
|
+
type: "string",
|
|
674
|
+
description: "The agent's Solana wallet address (base58 public key)"
|
|
675
|
+
},
|
|
676
|
+
agentName: {
|
|
677
|
+
type: "string",
|
|
678
|
+
description: 'Optional agent name to personalize the description. E.g. "Atlas"'
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
required: ["wallet"]
|
|
682
|
+
},
|
|
683
|
+
handler(params) {
|
|
684
|
+
return agentAppearancePrompt(
|
|
685
|
+
params.wallet,
|
|
686
|
+
params.agentName
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
var THEME_DESCRIPTIONS = {
|
|
691
|
+
solana: "Vibrant Solana brand colors (#14F195, #9945FF)",
|
|
692
|
+
dark: "Dark backgrounds with muted tones",
|
|
693
|
+
light: "Soft pastel backgrounds",
|
|
694
|
+
mono: "Full grayscale \u2014 all colors replaced with grays",
|
|
695
|
+
neon: "Cyberpunk high-contrast with neon accents and green border",
|
|
696
|
+
jupiter: "Jupiter aggregator dark blue palette with subtle border",
|
|
697
|
+
phantom: "Phantom wallet purple tones with subtle border",
|
|
698
|
+
circle: "Full circular border-radius (999px) for round avatars"
|
|
699
|
+
};
|
|
700
|
+
var listSolfaceThemes = {
|
|
701
|
+
name: "list_solface_themes",
|
|
702
|
+
description: "List all available SolFace preset themes with descriptions. Themes control colors, borders, backgrounds, and border-radius of generated avatars.",
|
|
703
|
+
parameters: {
|
|
704
|
+
type: "object",
|
|
705
|
+
properties: {}
|
|
706
|
+
},
|
|
707
|
+
handler() {
|
|
708
|
+
return Object.keys(PRESET_THEMES).map((name) => ({
|
|
709
|
+
name,
|
|
710
|
+
description: THEME_DESCRIPTIONS[name] ?? ""
|
|
711
|
+
}));
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
var SOLFACE_TOOLS = [
|
|
715
|
+
generateSolfaceSvg,
|
|
716
|
+
describeSolface,
|
|
717
|
+
getSolfaceTraits,
|
|
718
|
+
getAgentIdentity,
|
|
719
|
+
listSolfaceThemes
|
|
720
|
+
];
|
|
721
|
+
|
|
722
|
+
// src/agent/mcp-server.ts
|
|
723
|
+
function send(response) {
|
|
724
|
+
const json = JSON.stringify(response);
|
|
725
|
+
const msg = `Content-Length: ${Buffer.byteLength(json)}\r
|
|
726
|
+
\r
|
|
727
|
+
${json}`;
|
|
728
|
+
process.stdout.write(msg);
|
|
729
|
+
}
|
|
730
|
+
function handleRequest(req) {
|
|
731
|
+
const { id, method, params } = req;
|
|
732
|
+
if (method === "initialize") {
|
|
733
|
+
return {
|
|
734
|
+
jsonrpc: "2.0",
|
|
735
|
+
id,
|
|
736
|
+
result: {
|
|
737
|
+
protocolVersion: "2024-11-05",
|
|
738
|
+
capabilities: { tools: {} },
|
|
739
|
+
serverInfo: {
|
|
740
|
+
name: "solfaces",
|
|
741
|
+
version: "1.0.0"
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
if (method === "notifications/initialized") {
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
if (method === "tools/list") {
|
|
750
|
+
return {
|
|
751
|
+
jsonrpc: "2.0",
|
|
752
|
+
id,
|
|
753
|
+
result: {
|
|
754
|
+
tools: SOLFACE_TOOLS.map((t) => ({
|
|
755
|
+
name: t.name,
|
|
756
|
+
description: t.description,
|
|
757
|
+
inputSchema: t.parameters
|
|
758
|
+
}))
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
if (method === "tools/call") {
|
|
763
|
+
const p = params;
|
|
764
|
+
if (!p || typeof p.name !== "string") {
|
|
765
|
+
return {
|
|
766
|
+
jsonrpc: "2.0",
|
|
767
|
+
id,
|
|
768
|
+
error: { code: -32602, message: "Missing required 'name' parameter in tools/call" }
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
const toolName = p.name;
|
|
772
|
+
const toolArgs = p.arguments ?? {};
|
|
773
|
+
const tool = SOLFACE_TOOLS.find((t) => t.name === toolName);
|
|
774
|
+
if (!tool) {
|
|
775
|
+
return {
|
|
776
|
+
jsonrpc: "2.0",
|
|
777
|
+
id,
|
|
778
|
+
error: { code: -32601, message: `Unknown tool: ${toolName}` }
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
try {
|
|
782
|
+
const result = tool.handler(toolArgs);
|
|
783
|
+
const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
784
|
+
return {
|
|
785
|
+
jsonrpc: "2.0",
|
|
786
|
+
id,
|
|
787
|
+
result: {
|
|
788
|
+
content: [{ type: "text", text }]
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
} catch (err) {
|
|
792
|
+
return {
|
|
793
|
+
jsonrpc: "2.0",
|
|
794
|
+
id,
|
|
795
|
+
error: {
|
|
796
|
+
code: -32603,
|
|
797
|
+
message: err instanceof Error ? err.message : String(err)
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return {
|
|
803
|
+
jsonrpc: "2.0",
|
|
804
|
+
id,
|
|
805
|
+
error: { code: -32601, message: `Method not found: ${method}` }
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
var buffer = "";
|
|
809
|
+
process.stdin.setEncoding("utf-8");
|
|
810
|
+
process.stdin.on("data", (chunk) => {
|
|
811
|
+
buffer += chunk;
|
|
812
|
+
while (true) {
|
|
813
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
814
|
+
if (headerEnd === -1) break;
|
|
815
|
+
const header = buffer.slice(0, headerEnd);
|
|
816
|
+
const match = header.match(/Content-Length:\s*(\d+)/i);
|
|
817
|
+
if (!match) {
|
|
818
|
+
buffer = buffer.slice(headerEnd + 4);
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
const contentLength = parseInt(match[1], 10);
|
|
822
|
+
const bodyStart = headerEnd + 4;
|
|
823
|
+
if (buffer.length < bodyStart + contentLength) break;
|
|
824
|
+
const body = buffer.slice(bodyStart, bodyStart + contentLength);
|
|
825
|
+
buffer = buffer.slice(bodyStart + contentLength);
|
|
826
|
+
try {
|
|
827
|
+
const req = JSON.parse(body);
|
|
828
|
+
const res = handleRequest(req);
|
|
829
|
+
if (res && req.id !== void 0) {
|
|
830
|
+
send(res);
|
|
831
|
+
}
|
|
832
|
+
} catch {
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
process.stderr.write("SolFaces MCP server started\n");
|