spora 0.2.2 → 0.2.3
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/chunk-5ELTXYDV.js +162 -0
- package/dist/chunk-5ELTXYDV.js.map +1 -0
- package/dist/{chunk-3WPIVG27.js → chunk-IIQAE7OP.js} +3 -3
- package/dist/{chunk-CMD2AGVW.js → chunk-T3JHWIKX.js} +2 -2
- package/dist/cli.js +24 -24
- package/dist/{client-RII4ST5D.js → client-2CURS7J6.js} +7 -7
- package/dist/{client-FULMJH3S.js → client-2ZSXZE3D.js} +7 -7
- package/dist/{colony-CBUE2M3P.js → colony-EFGAMLOX.js} +7 -7
- package/dist/{heartbeat-4EMODFIB.js → heartbeat-IYMR7BNA.js} +16 -138
- package/dist/heartbeat-IYMR7BNA.js.map +1 -0
- package/dist/{init-ZEILF6Y5.js → init-CH72XB55.js} +5 -5
- package/dist/mcp-server.js +31 -31
- package/dist/prompt-builder-XVMRRGY3.js +17 -0
- package/dist/{queue-5SDR3MF2.js → queue-N64QLRAB.js} +2 -2
- package/dist/web-chat/chat.html +140 -113
- package/dist/{web-chat-BNAFTFTH.js → web-chat-XJQ42L3R.js} +35 -11
- package/dist/web-chat-XJQ42L3R.js.map +1 -0
- package/dist/{x-client-T762KJFE.js → x-client-SLAC2ON5.js} +2 -2
- package/dist/x-client-SLAC2ON5.js.map +1 -0
- package/package.json +1 -1
- package/dist/heartbeat-4EMODFIB.js.map +0 -1
- package/dist/web-chat-BNAFTFTH.js.map +0 -1
- /package/dist/{chunk-3WPIVG27.js.map → chunk-IIQAE7OP.js.map} +0 -0
- /package/dist/{chunk-CMD2AGVW.js.map → chunk-T3JHWIKX.js.map} +0 -0
- /package/dist/{client-RII4ST5D.js.map → client-2CURS7J6.js.map} +0 -0
- /package/dist/{client-FULMJH3S.js.map → client-2ZSXZE3D.js.map} +0 -0
- /package/dist/{colony-CBUE2M3P.js.map → colony-EFGAMLOX.js.map} +0 -0
- /package/dist/{init-ZEILF6Y5.js.map → init-CH72XB55.js.map} +0 -0
- /package/dist/{queue-5SDR3MF2.js.map → prompt-builder-XVMRRGY3.js.map} +0 -0
- /package/dist/{x-client-T762KJFE.js.map → queue-N64QLRAB.js.map} +0 -0
package/dist/web-chat/chat.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Spora
|
|
6
|
+
<title>Spora</title>
|
|
7
7
|
<style>
|
|
8
8
|
* {
|
|
9
9
|
margin: 0;
|
|
@@ -21,39 +21,46 @@
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
.header {
|
|
24
|
-
padding:
|
|
25
|
-
border-bottom: 1px solid rgba(255, 255, 255, 0.
|
|
24
|
+
padding: 0.875rem 1.25rem;
|
|
25
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
|
26
26
|
display: flex;
|
|
27
27
|
align-items: center;
|
|
28
|
-
gap:
|
|
28
|
+
gap: 0.75rem;
|
|
29
|
+
background: #0a0a0a;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.logo-img {
|
|
33
|
+
height: 28px;
|
|
34
|
+
width: auto;
|
|
29
35
|
}
|
|
30
36
|
|
|
31
|
-
.logo {
|
|
32
|
-
font-size: 1.
|
|
33
|
-
font-weight:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-webkit-text-fill-color: transparent;
|
|
37
|
-
background-clip: text;
|
|
37
|
+
.logo-text {
|
|
38
|
+
font-size: 1.125rem;
|
|
39
|
+
font-weight: 700;
|
|
40
|
+
letter-spacing: -0.02em;
|
|
41
|
+
color: #fff;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
.
|
|
44
|
+
.header-right {
|
|
41
45
|
margin-left: auto;
|
|
42
46
|
display: flex;
|
|
43
47
|
align-items: center;
|
|
44
48
|
gap: 0.5rem;
|
|
45
|
-
font-size: 0.875rem;
|
|
46
|
-
color: rgba(255, 255, 255, 0.6);
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
.status-dot {
|
|
50
|
-
width:
|
|
51
|
-
height:
|
|
52
|
+
width: 7px;
|
|
53
|
+
height: 7px;
|
|
52
54
|
border-radius: 50%;
|
|
53
|
-
background: #
|
|
55
|
+
background: #389e77;
|
|
54
56
|
animation: pulse 2s infinite;
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
.status-text {
|
|
60
|
+
font-size: 0.75rem;
|
|
61
|
+
color: rgba(255, 255, 255, 0.4);
|
|
62
|
+
}
|
|
63
|
+
|
|
57
64
|
@keyframes pulse {
|
|
58
65
|
0%, 100% { opacity: 1; }
|
|
59
66
|
50% { opacity: 0.5; }
|
|
@@ -62,20 +69,33 @@
|
|
|
62
69
|
.chat-container {
|
|
63
70
|
flex: 1;
|
|
64
71
|
overflow-y: auto;
|
|
65
|
-
padding:
|
|
72
|
+
padding: 1.25rem;
|
|
66
73
|
display: flex;
|
|
67
74
|
flex-direction: column;
|
|
68
|
-
gap:
|
|
75
|
+
gap: 0.875rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.chat-container::-webkit-scrollbar {
|
|
79
|
+
width: 4px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.chat-container::-webkit-scrollbar-track {
|
|
83
|
+
background: transparent;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.chat-container::-webkit-scrollbar-thumb {
|
|
87
|
+
background: rgba(255, 255, 255, 0.1);
|
|
88
|
+
border-radius: 4px;
|
|
69
89
|
}
|
|
70
90
|
|
|
71
91
|
.message {
|
|
72
92
|
display: flex;
|
|
73
|
-
gap: 0.
|
|
93
|
+
gap: 0.625rem;
|
|
74
94
|
animation: fadeIn 0.3s ease-in;
|
|
75
95
|
}
|
|
76
96
|
|
|
77
97
|
@keyframes fadeIn {
|
|
78
|
-
from { opacity: 0; transform: translateY(
|
|
98
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
79
99
|
to { opacity: 1; transform: translateY(0); }
|
|
80
100
|
}
|
|
81
101
|
|
|
@@ -88,15 +108,16 @@
|
|
|
88
108
|
}
|
|
89
109
|
|
|
90
110
|
.message-avatar {
|
|
91
|
-
width:
|
|
92
|
-
height:
|
|
111
|
+
width: 32px;
|
|
112
|
+
height: 32px;
|
|
93
113
|
border-radius: 50%;
|
|
94
|
-
background: #
|
|
114
|
+
background: #1a1a1a;
|
|
95
115
|
display: flex;
|
|
96
116
|
align-items: center;
|
|
97
117
|
justify-content: center;
|
|
98
118
|
flex-shrink: 0;
|
|
99
119
|
overflow: hidden;
|
|
120
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
100
121
|
}
|
|
101
122
|
|
|
102
123
|
.message-avatar img {
|
|
@@ -106,87 +127,93 @@
|
|
|
106
127
|
}
|
|
107
128
|
|
|
108
129
|
.message.user .message-avatar {
|
|
109
|
-
|
|
130
|
+
display: none;
|
|
110
131
|
}
|
|
111
132
|
|
|
112
133
|
.message-content {
|
|
113
|
-
max-width:
|
|
114
|
-
padding: 0.
|
|
115
|
-
border-radius:
|
|
116
|
-
font-size: 0.
|
|
117
|
-
line-height: 1.
|
|
134
|
+
max-width: 80%;
|
|
135
|
+
padding: 0.625rem 0.875rem;
|
|
136
|
+
border-radius: 0.875rem;
|
|
137
|
+
font-size: 0.8125rem;
|
|
138
|
+
line-height: 1.55;
|
|
118
139
|
white-space: pre-wrap;
|
|
119
140
|
}
|
|
120
141
|
|
|
121
142
|
.message.user .message-content {
|
|
122
|
-
background:
|
|
143
|
+
background: #389e77;
|
|
123
144
|
color: #fff;
|
|
124
145
|
border-bottom-right-radius: 0.25rem;
|
|
125
146
|
}
|
|
126
147
|
|
|
127
148
|
.message.assistant .message-content {
|
|
128
|
-
background: #
|
|
129
|
-
border: 1px solid rgba(255, 255, 255, 0.
|
|
149
|
+
background: #141414;
|
|
150
|
+
border: 1px solid rgba(255, 255, 255, 0.06);
|
|
130
151
|
border-bottom-left-radius: 0.25rem;
|
|
152
|
+
color: rgba(255, 255, 255, 0.9);
|
|
131
153
|
}
|
|
132
154
|
|
|
133
155
|
.input-container {
|
|
134
|
-
padding: 1.
|
|
135
|
-
border-top: 1px solid rgba(255, 255, 255, 0.
|
|
156
|
+
padding: 1rem 1.25rem;
|
|
157
|
+
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
|
136
158
|
display: flex;
|
|
137
|
-
gap: 0.
|
|
159
|
+
gap: 0.5rem;
|
|
160
|
+
background: #0a0a0a;
|
|
138
161
|
}
|
|
139
162
|
|
|
140
163
|
.input-box {
|
|
141
164
|
flex: 1;
|
|
142
|
-
background: #
|
|
143
|
-
border: 1px solid rgba(255, 255, 255, 0.
|
|
144
|
-
border-radius: 0.
|
|
145
|
-
padding: 0.
|
|
165
|
+
background: #141414;
|
|
166
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
167
|
+
border-radius: 0.625rem;
|
|
168
|
+
padding: 0.625rem 0.875rem;
|
|
146
169
|
color: #fff;
|
|
147
|
-
font-size: 0.
|
|
170
|
+
font-size: 0.8125rem;
|
|
148
171
|
font-family: inherit;
|
|
149
172
|
outline: none;
|
|
150
173
|
transition: border-color 0.2s;
|
|
151
174
|
}
|
|
152
175
|
|
|
153
176
|
.input-box:focus {
|
|
154
|
-
border-color: #
|
|
177
|
+
border-color: #389e77;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.input-box::placeholder {
|
|
181
|
+
color: rgba(255, 255, 255, 0.25);
|
|
155
182
|
}
|
|
156
183
|
|
|
157
184
|
.send-button {
|
|
158
|
-
background:
|
|
185
|
+
background: #389e77;
|
|
159
186
|
color: #fff;
|
|
160
187
|
border: none;
|
|
161
|
-
padding: 0.
|
|
162
|
-
border-radius: 0.
|
|
188
|
+
padding: 0.625rem 1.125rem;
|
|
189
|
+
border-radius: 0.625rem;
|
|
163
190
|
font-weight: 600;
|
|
164
|
-
font-size: 0.
|
|
191
|
+
font-size: 0.8125rem;
|
|
165
192
|
cursor: pointer;
|
|
166
|
-
transition:
|
|
193
|
+
transition: background 0.2s, transform 0.15s;
|
|
167
194
|
}
|
|
168
195
|
|
|
169
196
|
.send-button:hover:not(:disabled) {
|
|
170
|
-
|
|
171
|
-
|
|
197
|
+
background: #4ab88a;
|
|
198
|
+
transform: scale(1.02);
|
|
172
199
|
}
|
|
173
200
|
|
|
174
201
|
.send-button:disabled {
|
|
175
|
-
opacity: 0.
|
|
202
|
+
opacity: 0.4;
|
|
176
203
|
cursor: not-allowed;
|
|
177
204
|
}
|
|
178
205
|
|
|
179
206
|
.loading {
|
|
180
207
|
display: flex;
|
|
181
208
|
gap: 0.25rem;
|
|
182
|
-
padding: 0.
|
|
209
|
+
padding: 0.5rem 0;
|
|
183
210
|
}
|
|
184
211
|
|
|
185
212
|
.loading-dot {
|
|
186
|
-
width:
|
|
187
|
-
height:
|
|
213
|
+
width: 6px;
|
|
214
|
+
height: 6px;
|
|
188
215
|
border-radius: 50%;
|
|
189
|
-
background: rgba(255, 255, 255, 0.
|
|
216
|
+
background: rgba(255, 255, 255, 0.3);
|
|
190
217
|
animation: bounce 1.4s infinite ease-in-out both;
|
|
191
218
|
}
|
|
192
219
|
|
|
@@ -197,47 +224,20 @@
|
|
|
197
224
|
0%, 80%, 100% { transform: scale(0); }
|
|
198
225
|
40% { transform: scale(1); }
|
|
199
226
|
}
|
|
200
|
-
|
|
201
|
-
.empty-state {
|
|
202
|
-
flex: 1;
|
|
203
|
-
display: flex;
|
|
204
|
-
flex-direction: column;
|
|
205
|
-
align-items: center;
|
|
206
|
-
justify-content: center;
|
|
207
|
-
text-align: center;
|
|
208
|
-
gap: 1rem;
|
|
209
|
-
color: rgba(255, 255, 255, 0.4);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.empty-state-icon {
|
|
213
|
-
font-size: 4rem;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
.empty-state-text {
|
|
217
|
-
font-size: 1.125rem;
|
|
218
|
-
font-weight: 600;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.empty-state-subtext {
|
|
222
|
-
font-size: 0.875rem;
|
|
223
|
-
}
|
|
224
227
|
</style>
|
|
225
228
|
</head>
|
|
226
229
|
<body>
|
|
227
230
|
<div class="header">
|
|
228
|
-
<
|
|
229
|
-
|
|
231
|
+
<svg class="logo-img" viewBox="0 0 120 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
232
|
+
<text x="0" y="22" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="22" font-weight="800" letter-spacing="-0.5" fill="#fff">spora</text>
|
|
233
|
+
</svg>
|
|
234
|
+
<div class="header-right">
|
|
230
235
|
<span class="status-dot"></span>
|
|
231
|
-
<span>
|
|
236
|
+
<span class="status-text">Online</span>
|
|
232
237
|
</div>
|
|
233
238
|
</div>
|
|
234
239
|
|
|
235
240
|
<div class="chat-container" id="chatContainer">
|
|
236
|
-
<div class="empty-state">
|
|
237
|
-
<div class="empty-state-icon">💬</div>
|
|
238
|
-
<div class="empty-state-text">Chat with your Spore</div>
|
|
239
|
-
<div class="empty-state-subtext">Start a conversation below</div>
|
|
240
|
-
</div>
|
|
241
241
|
</div>
|
|
242
242
|
|
|
243
243
|
<div class="input-container">
|
|
@@ -245,7 +245,7 @@
|
|
|
245
245
|
type="text"
|
|
246
246
|
class="input-box"
|
|
247
247
|
id="messageInput"
|
|
248
|
-
placeholder="
|
|
248
|
+
placeholder="Message your agent..."
|
|
249
249
|
autocomplete="off"
|
|
250
250
|
/>
|
|
251
251
|
<button class="send-button" id="sendButton">Send</button>
|
|
@@ -257,13 +257,38 @@
|
|
|
257
257
|
const sendButton = document.getElementById('sendButton');
|
|
258
258
|
|
|
259
259
|
let isLoading = false;
|
|
260
|
+
let agentName = 'Your Spore';
|
|
261
|
+
let agentPfp = null;
|
|
262
|
+
|
|
263
|
+
// Fetch agent identity and show welcome message
|
|
264
|
+
async function init() {
|
|
265
|
+
try {
|
|
266
|
+
const response = await fetch('/api/identity');
|
|
267
|
+
const data = await response.json();
|
|
268
|
+
if (data.identity) {
|
|
269
|
+
agentName = data.identity.name || 'Your Spore';
|
|
270
|
+
agentPfp = data.identity.profileImage || null;
|
|
271
|
+
|
|
272
|
+
// Update page title
|
|
273
|
+
document.title = `${agentName} - Spora`;
|
|
274
|
+
}
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error('Failed to load identity:', error);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Show welcome message from the agent
|
|
280
|
+
addMessage('assistant', `Hey! I'm ${agentName}. I've just been set up and I'm ready to start posting on X. What would you like me to do? You can tell me what to post, how to behave, or just chat with me.`);
|
|
281
|
+
|
|
282
|
+
// Load any existing messages
|
|
283
|
+
await loadMessages();
|
|
284
|
+
}
|
|
260
285
|
|
|
261
|
-
// Load existing messages on startup
|
|
262
286
|
async function loadMessages() {
|
|
263
287
|
try {
|
|
264
288
|
const response = await fetch('/api/messages');
|
|
265
289
|
const data = await response.json();
|
|
266
290
|
if (data.messages && data.messages.length > 0) {
|
|
291
|
+
// Clear welcome message if there are existing messages
|
|
267
292
|
chatContainer.innerHTML = '';
|
|
268
293
|
data.messages.forEach(msg => {
|
|
269
294
|
addMessage(msg.role, msg.content, false);
|
|
@@ -275,28 +300,29 @@
|
|
|
275
300
|
}
|
|
276
301
|
|
|
277
302
|
function addMessage(role, content, animate = true) {
|
|
278
|
-
// Remove empty state if it exists
|
|
279
|
-
const emptyState = chatContainer.querySelector('.empty-state');
|
|
280
|
-
if (emptyState) {
|
|
281
|
-
emptyState.remove();
|
|
282
|
-
}
|
|
283
|
-
|
|
284
303
|
const messageDiv = document.createElement('div');
|
|
285
304
|
messageDiv.className = `message ${role}`;
|
|
305
|
+
if (!animate) messageDiv.style.animation = 'none';
|
|
286
306
|
|
|
287
|
-
const avatar = document.createElement('div');
|
|
288
|
-
avatar.className = 'message-avatar';
|
|
289
307
|
if (role === 'assistant') {
|
|
290
|
-
avatar
|
|
291
|
-
|
|
292
|
-
|
|
308
|
+
const avatar = document.createElement('div');
|
|
309
|
+
avatar.className = 'message-avatar';
|
|
310
|
+
if (agentPfp) {
|
|
311
|
+
avatar.innerHTML = `<img src="${agentPfp}" alt="${agentName}" onerror="this.parentElement.textContent='${agentName.charAt(0).toUpperCase()}'" />`;
|
|
312
|
+
} else {
|
|
313
|
+
avatar.textContent = agentName.charAt(0).toUpperCase();
|
|
314
|
+
avatar.style.background = '#389e77';
|
|
315
|
+
avatar.style.color = '#fff';
|
|
316
|
+
avatar.style.fontSize = '0.75rem';
|
|
317
|
+
avatar.style.fontWeight = '700';
|
|
318
|
+
}
|
|
319
|
+
messageDiv.appendChild(avatar);
|
|
293
320
|
}
|
|
294
321
|
|
|
295
322
|
const contentDiv = document.createElement('div');
|
|
296
323
|
contentDiv.className = 'message-content';
|
|
297
324
|
contentDiv.textContent = content;
|
|
298
325
|
|
|
299
|
-
messageDiv.appendChild(avatar);
|
|
300
326
|
messageDiv.appendChild(contentDiv);
|
|
301
327
|
|
|
302
328
|
chatContainer.appendChild(messageDiv);
|
|
@@ -310,7 +336,15 @@
|
|
|
310
336
|
|
|
311
337
|
const avatar = document.createElement('div');
|
|
312
338
|
avatar.className = 'message-avatar';
|
|
313
|
-
|
|
339
|
+
if (agentPfp) {
|
|
340
|
+
avatar.innerHTML = `<img src="${agentPfp}" alt="${agentName}" />`;
|
|
341
|
+
} else {
|
|
342
|
+
avatar.textContent = agentName.charAt(0).toUpperCase();
|
|
343
|
+
avatar.style.background = '#389e77';
|
|
344
|
+
avatar.style.color = '#fff';
|
|
345
|
+
avatar.style.fontSize = '0.75rem';
|
|
346
|
+
avatar.style.fontWeight = '700';
|
|
347
|
+
}
|
|
314
348
|
|
|
315
349
|
const loadingContent = document.createElement('div');
|
|
316
350
|
loadingContent.className = 'message-content';
|
|
@@ -331,9 +365,7 @@
|
|
|
331
365
|
|
|
332
366
|
function hideLoading() {
|
|
333
367
|
const loadingDiv = document.getElementById('loadingMessage');
|
|
334
|
-
if (loadingDiv)
|
|
335
|
-
loadingDiv.remove();
|
|
336
|
-
}
|
|
368
|
+
if (loadingDiv) loadingDiv.remove();
|
|
337
369
|
}
|
|
338
370
|
|
|
339
371
|
async function sendMessage() {
|
|
@@ -344,11 +376,9 @@
|
|
|
344
376
|
sendButton.disabled = true;
|
|
345
377
|
messageInput.disabled = true;
|
|
346
378
|
|
|
347
|
-
// Add user message
|
|
348
379
|
addMessage('user', message);
|
|
349
380
|
messageInput.value = '';
|
|
350
381
|
|
|
351
|
-
// Show loading
|
|
352
382
|
showLoading();
|
|
353
383
|
|
|
354
384
|
try {
|
|
@@ -359,17 +389,16 @@
|
|
|
359
389
|
});
|
|
360
390
|
|
|
361
391
|
const data = await response.json();
|
|
362
|
-
|
|
363
392
|
hideLoading();
|
|
364
393
|
|
|
365
394
|
if (data.response) {
|
|
366
395
|
addMessage('assistant', data.response);
|
|
367
396
|
} else if (data.error) {
|
|
368
|
-
addMessage('assistant', '
|
|
397
|
+
addMessage('assistant', 'Something went wrong. Try again?');
|
|
369
398
|
}
|
|
370
399
|
} catch (error) {
|
|
371
400
|
hideLoading();
|
|
372
|
-
addMessage('assistant', '
|
|
401
|
+
addMessage('assistant', 'Couldn\'t connect to the server. Try again?');
|
|
373
402
|
} finally {
|
|
374
403
|
isLoading = false;
|
|
375
404
|
sendButton.disabled = false;
|
|
@@ -386,10 +415,8 @@
|
|
|
386
415
|
}
|
|
387
416
|
});
|
|
388
417
|
|
|
389
|
-
//
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
// Focus input
|
|
418
|
+
// Initialize
|
|
419
|
+
init();
|
|
393
420
|
messageInput.focus();
|
|
394
421
|
</script>
|
|
395
422
|
</body>
|
|
@@ -16,9 +16,13 @@ var WebChatServer = class {
|
|
|
16
16
|
port;
|
|
17
17
|
messages = [];
|
|
18
18
|
onUserMessage;
|
|
19
|
+
identity;
|
|
19
20
|
constructor(port = 3737) {
|
|
20
21
|
this.port = port;
|
|
21
22
|
}
|
|
23
|
+
setIdentity(identity) {
|
|
24
|
+
this.identity = identity;
|
|
25
|
+
}
|
|
22
26
|
setMessageHandler(handler) {
|
|
23
27
|
this.onUserMessage = handler;
|
|
24
28
|
}
|
|
@@ -65,6 +69,11 @@ var WebChatServer = class {
|
|
|
65
69
|
}
|
|
66
70
|
return;
|
|
67
71
|
}
|
|
72
|
+
if (url.pathname === "/api/identity" && req.method === "GET") {
|
|
73
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
74
|
+
res.end(JSON.stringify({ identity: this.identity || null }));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
68
77
|
if (url.pathname === "/api/messages" && req.method === "GET") {
|
|
69
78
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
70
79
|
res.end(JSON.stringify({ messages: this.messages }));
|
|
@@ -135,17 +144,32 @@ import chalk from "chalk";
|
|
|
135
144
|
async function startWebChat() {
|
|
136
145
|
const identity = loadIdentity();
|
|
137
146
|
const server = new WebChatServer();
|
|
147
|
+
server.setIdentity({
|
|
148
|
+
name: identity.name,
|
|
149
|
+
handle: identity.handle,
|
|
150
|
+
bio: identity.bio,
|
|
151
|
+
profileImage: identity.profileImage
|
|
152
|
+
});
|
|
153
|
+
const chatHistory = [];
|
|
154
|
+
let systemPrompt = null;
|
|
138
155
|
server.setMessageHandler(async (message) => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
156
|
+
try {
|
|
157
|
+
if (!systemPrompt) {
|
|
158
|
+
const { buildChatPrompt } = await import("./prompt-builder-XVMRRGY3.js");
|
|
159
|
+
systemPrompt = buildChatPrompt();
|
|
160
|
+
}
|
|
161
|
+
const { hasLLMKey, chat: chatLLM } = await import("./llm-OH2Z4PSN.js");
|
|
162
|
+
if (!hasLLMKey()) {
|
|
163
|
+
return "I can't respond right now - no API key configured. Run `spora set-llm-key` to set one up.";
|
|
164
|
+
}
|
|
165
|
+
chatHistory.push({ role: "user", content: message });
|
|
166
|
+
const response = await chatLLM(systemPrompt, chatHistory);
|
|
167
|
+
chatHistory.push({ role: "assistant", content: response.content });
|
|
168
|
+
return response.content;
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error("Chat error:", error);
|
|
171
|
+
return `Sorry, I ran into an issue: ${error.message}`;
|
|
172
|
+
}
|
|
149
173
|
});
|
|
150
174
|
const url = await server.start();
|
|
151
175
|
console.log(chalk.green(`
|
|
@@ -195,4 +219,4 @@ export {
|
|
|
195
219
|
openBrowser,
|
|
196
220
|
startWebChat
|
|
197
221
|
};
|
|
198
|
-
//# sourceMappingURL=web-chat-
|
|
222
|
+
//# sourceMappingURL=web-chat-XJQ42L3R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/web-chat/server.ts","../src/web-chat/index.ts"],"sourcesContent":["/**\n * Local web chat server\n * Serves a simple chat interface for interacting with your Spore\n */\n\nimport http from \"node:http\";\nimport { URL } from \"node:url\";\nimport { readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\ninterface ChatMessage {\n role: \"user\" | \"assistant\";\n content: string;\n timestamp: number;\n}\n\ninterface AgentIdentity {\n name: string;\n handle: string;\n bio?: string;\n profileImage?: string;\n}\n\nexport class WebChatServer {\n private server: http.Server | null = null;\n private port: number;\n private messages: ChatMessage[] = [];\n private onUserMessage?: (message: string) => Promise<string>;\n private identity?: AgentIdentity;\n\n constructor(port = 3737) {\n this.port = port;\n }\n\n setIdentity(identity: AgentIdentity) {\n this.identity = identity;\n }\n\n setMessageHandler(handler: (message: string) => Promise<string>) {\n this.onUserMessage = handler;\n }\n\n async start(): Promise<string> {\n return new Promise((resolve, reject) => {\n this.server = http.createServer(async (req, res) => {\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n\n // CORS headers\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(200);\n res.end();\n return;\n }\n\n // Serve HTML\n if (url.pathname === \"/\" || url.pathname === \"/index.html\") {\n try {\n // Try multiple paths since __dirname changes based on build output\n const possiblePaths = [\n join(__dirname, \"web-chat\", \"chat.html\"),\n join(__dirname, \"chat.html\"),\n join(__dirname, \"..\", \"web-chat\", \"chat.html\"),\n join(__dirname, \"..\", \"src\", \"web-chat\", \"chat.html\"),\n ];\n\n let html: string | null = null;\n for (const p of possiblePaths) {\n try {\n html = readFileSync(p, \"utf-8\");\n break;\n } catch {\n continue;\n }\n }\n\n if (html) {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(html);\n } else {\n console.error(\"Could not find chat.html in any of:\", possiblePaths);\n res.writeHead(500);\n res.end(\"Error: Could not find chat.html\");\n }\n } catch (error) {\n res.writeHead(500);\n res.end(\"Error loading chat interface\");\n }\n return;\n }\n\n // API: Get agent identity\n if (url.pathname === \"/api/identity\" && req.method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ identity: this.identity || null }));\n return;\n }\n\n // API: Get messages\n if (url.pathname === \"/api/messages\" && req.method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ messages: this.messages }));\n return;\n }\n\n // API: Send message\n if (url.pathname === \"/api/message\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => {\n body += chunk.toString();\n });\n\n req.on(\"end\", async () => {\n try {\n const { message } = JSON.parse(body);\n\n // Add user message\n this.messages.push({\n role: \"user\",\n content: message,\n timestamp: Date.now(),\n });\n\n // Get response\n if (this.onUserMessage) {\n const response = await this.onUserMessage(message);\n this.messages.push({\n role: \"assistant\",\n content: response,\n timestamp: Date.now(),\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ success: true, response }));\n } else {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No message handler configured\" }));\n }\n } catch (error) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid request\" }));\n }\n });\n return;\n }\n\n // 404\n res.writeHead(404);\n res.end(\"Not found\");\n });\n\n this.server.listen(this.port, () => {\n const url = `http://localhost:${this.port}`;\n resolve(url);\n });\n\n this.server.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"EADDRINUSE\") {\n // Port in use, try next port\n this.port++;\n this.server?.close();\n this.start().then(resolve).catch(reject);\n } else {\n reject(error);\n }\n });\n });\n }\n\n stop() {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n }\n}\n","/**\n * Web chat integration\n */\n\nimport { WebChatServer } from \"./server.js\";\nimport { loadIdentity } from \"../identity/index.js\";\nimport { execSync } from \"node:child_process\";\nimport chalk from \"chalk\";\n\nexport async function startWebChat() {\n const identity = loadIdentity();\n\n const server = new WebChatServer();\n\n // Pass identity to server so the chat UI can display it\n server.setIdentity({\n name: identity.name,\n handle: identity.handle,\n bio: identity.bio,\n profileImage: identity.profileImage,\n });\n\n // Set up message handler - connected to actual LLM\n const chatHistory: Array<{ role: \"user\" | \"assistant\"; content: string }> = [];\n let systemPrompt: string | null = null;\n\n server.setMessageHandler(async (message: string) => {\n try {\n // Build system prompt on first message\n if (!systemPrompt) {\n const { buildChatPrompt } = await import(\"../runtime/prompt-builder.js\");\n systemPrompt = buildChatPrompt();\n }\n\n // Check for LLM key\n const { hasLLMKey, chat: chatLLM } = await import(\"../runtime/llm.js\");\n if (!hasLLMKey()) {\n return \"I can't respond right now - no API key configured. Run `spora set-llm-key` to set one up.\";\n }\n\n // Add user message to history\n chatHistory.push({ role: \"user\", content: message });\n\n // Call LLM with full conversation history\n const response = await chatLLM(systemPrompt, chatHistory);\n\n // Add assistant response to history\n chatHistory.push({ role: \"assistant\", content: response.content });\n\n return response.content;\n } catch (error) {\n console.error(\"Chat error:\", error);\n return `Sorry, I ran into an issue: ${(error as Error).message}`;\n }\n });\n\n const url = await server.start();\n\n console.log(chalk.green(`\\n✓ Chat interface started at ${chalk.bold(url)}\\n`));\n console.log(chalk.dim(`Press Ctrl+C to stop the server\\n`));\n\n // Open browser\n openBrowser(url);\n\n // Keep process alive\n process.on(\"SIGINT\", () => {\n console.log(chalk.yellow(\"\\n\\nStopping chat server...\"));\n server.stop();\n process.exit(0);\n });\n\n // Return the server instance for external control\n return server;\n}\n\n/**\n * Open URL in a separate browser window (app-like)\n */\nfunction openBrowser(url: string) {\n const platform = process.platform;\n\n try {\n if (platform === \"darwin\") {\n // Open as a standalone Chrome app window (smaller, separate from existing tabs)\n try {\n execSync(`open -na \"Google Chrome\" --args --app=\"${url}\" --window-size=500,700`, { stdio: \"ignore\" });\n } catch {\n // Fallback: try Chromium-based browsers, then default\n try {\n execSync(`open -na \"Brave Browser\" --args --app=\"${url}\" --window-size=500,700`, { stdio: \"ignore\" });\n } catch {\n execSync(`open \"${url}\"`, { stdio: \"ignore\" });\n }\n }\n } else if (platform === \"win32\") {\n try {\n execSync(`start chrome --app=\"${url}\" --window-size=500,700`, { stdio: \"ignore\" });\n } catch {\n execSync(`start \"\" \"${url}\"`, { stdio: \"ignore\" });\n }\n } else {\n // Linux\n try {\n execSync(`google-chrome --app=\"${url}\" --window-size=500,700`, { stdio: \"ignore\" });\n } catch {\n execSync(`xdg-open \"${url}\"`, { stdio: \"ignore\" });\n }\n }\n } catch (error) {\n // Browser couldn't be opened, that's okay\n console.log(chalk.dim(`(Couldn't open browser automatically - please visit ${url} manually)`));\n }\n}\n\nexport { openBrowser };\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getXClient,
|
|
3
3
|
resetXClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-IIQAE7OP.js";
|
|
5
5
|
import "./chunk-YEKHNTQO.js";
|
|
6
6
|
import "./chunk-KELPENM3.js";
|
|
7
7
|
import "./chunk-53YLFYJF.js";
|
|
@@ -9,4 +9,4 @@ export {
|
|
|
9
9
|
getXClient,
|
|
10
10
|
resetXClient
|
|
11
11
|
};
|
|
12
|
-
//# sourceMappingURL=x-client-
|
|
12
|
+
//# sourceMappingURL=x-client-SLAC2ON5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|