squidclaw 0.5.1 → 0.5.2
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/lib/cli/hatch-tui.js +124 -48
- package/lib/cli/wa-login.js +1 -1
- package/package.json +1 -1
package/lib/cli/hatch-tui.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import { createInterface } from 'readline';
|
|
8
|
-
import { loadConfig, getHome } from '../core/config.js';
|
|
8
|
+
import { loadConfig, getHome, MODEL_MAP } from '../core/config.js';
|
|
9
9
|
import { writeFileSync, readFileSync, existsSync } from 'fs';
|
|
10
10
|
import { join } from 'path';
|
|
11
11
|
|
|
@@ -18,7 +18,7 @@ export async function hatchTUI(agentId) {
|
|
|
18
18
|
const manifestPath = join(agentDir, 'agent.json');
|
|
19
19
|
|
|
20
20
|
if (!existsSync(manifestPath)) {
|
|
21
|
-
console.log(chalk.red('Agent not found: ' + agentId));
|
|
21
|
+
console.log(chalk.red(' Agent not found: ' + agentId));
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -39,10 +39,9 @@ export async function hatchTUI(agentId) {
|
|
|
39
39
|
console.log();
|
|
40
40
|
|
|
41
41
|
// ═══ Start engine silently ═══
|
|
42
|
-
let engine = null;
|
|
43
42
|
const port = config.engine?.port || 9500;
|
|
43
|
+
let engine = null;
|
|
44
44
|
|
|
45
|
-
// Check if engine already running
|
|
46
45
|
let engineReady = false;
|
|
47
46
|
try {
|
|
48
47
|
const res = await fetch('http://127.0.0.1:' + port + '/health');
|
|
@@ -54,7 +53,6 @@ export async function hatchTUI(agentId) {
|
|
|
54
53
|
try {
|
|
55
54
|
const { SquidclawEngine } = await import('../engine.js');
|
|
56
55
|
engine = new SquidclawEngine({ port });
|
|
57
|
-
// Suppress output
|
|
58
56
|
const origLog = console.log;
|
|
59
57
|
const origErr = console.error;
|
|
60
58
|
console.log = () => {};
|
|
@@ -64,7 +62,8 @@ export async function hatchTUI(agentId) {
|
|
|
64
62
|
console.error = origErr;
|
|
65
63
|
process.stdout.write(chalk.green(' ready!\n\n'));
|
|
66
64
|
} catch (err) {
|
|
67
|
-
|
|
65
|
+
process.stdout.write(chalk.red(' failed\n'));
|
|
66
|
+
console.log(chalk.red(' ' + err.message));
|
|
68
67
|
console.log(chalk.gray(' Run "squidclaw start" first, then "squidclaw wake"'));
|
|
69
68
|
return;
|
|
70
69
|
}
|
|
@@ -72,10 +71,9 @@ export async function hatchTUI(agentId) {
|
|
|
72
71
|
|
|
73
72
|
await DELAY(500);
|
|
74
73
|
|
|
75
|
-
// ═══
|
|
76
|
-
const firstMessage = await
|
|
77
|
-
'You just came to life
|
|
78
|
-
true
|
|
74
|
+
// ═══ Agent's first message — call AI directly ═══
|
|
75
|
+
const firstMessage = await callAgentDirect(config, manifest,
|
|
76
|
+
'You just came to life. You are ' + manifest.name + '. Express excitement — be curious, playful. Ask who you are talking to. 2-3 SHORT sentences only.'
|
|
79
77
|
);
|
|
80
78
|
|
|
81
79
|
await typeMessage(manifest.name, firstMessage);
|
|
@@ -84,38 +82,36 @@ export async function hatchTUI(agentId) {
|
|
|
84
82
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
85
83
|
let turnCount = 0;
|
|
86
84
|
let agentFinalName = manifest.name;
|
|
85
|
+
const chatHistory = [
|
|
86
|
+
{ role: 'assistant', content: firstMessage }
|
|
87
|
+
];
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
rl.question(chalk.green('\n You: '), (answer) => resolve(answer));
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
console.log(chalk.gray('\n (Type /done when you\'re ready to finish hatching)\n'));
|
|
89
|
+
console.log(chalk.gray('\n (Type /done when ready to finish hatching)\n'));
|
|
93
90
|
|
|
94
91
|
while (true) {
|
|
95
|
-
const userInput = await
|
|
92
|
+
const userInput = await new Promise(resolve => {
|
|
93
|
+
rl.question(chalk.green('\n You: '), resolve);
|
|
94
|
+
});
|
|
96
95
|
if (!userInput || !userInput.trim()) continue;
|
|
97
96
|
if (userInput.trim().toLowerCase() === '/done' || userInput.trim().toLowerCase() === '/exit') break;
|
|
98
97
|
|
|
99
98
|
turnCount++;
|
|
99
|
+
chatHistory.push({ role: 'user', content: userInput });
|
|
100
100
|
|
|
101
101
|
let hint = '';
|
|
102
|
-
if (turnCount <= 2)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
hint = 'You are forming your identity now. Confirm what you have learned. Show your personality emerging.';
|
|
106
|
-
} else {
|
|
107
|
-
hint = 'You know who you are. Act like it. If conversation feels complete, suggest typing /done.';
|
|
108
|
-
}
|
|
102
|
+
if (turnCount <= 2) hint = 'You are discovering who you are. Ask about your name, personality, purpose. Be curious.';
|
|
103
|
+
else if (turnCount <= 4) hint = 'Your identity is forming. Show your personality. Confirm what you learned.';
|
|
104
|
+
else hint = 'You know who you are now. Act like it. If the conversation feels complete, suggest /done.';
|
|
109
105
|
|
|
110
|
-
const response = await
|
|
106
|
+
const response = await callAgentDirect(config, manifest, userInput, chatHistory, hint);
|
|
107
|
+
chatHistory.push({ role: 'assistant', content: response });
|
|
111
108
|
await typeMessage(agentFinalName, response);
|
|
112
109
|
|
|
113
|
-
|
|
114
|
-
const nameMatch = userInput.match(/call you (\w+)/i) || userInput.match(/your name.+?(\w+)$/i) || userInput.match(/name you (\w+)/i);
|
|
110
|
+
const nameMatch = userInput.match(/call you (\w+)/i) || userInput.match(/name you (\w+)/i);
|
|
115
111
|
if (nameMatch) agentFinalName = nameMatch[1];
|
|
116
112
|
}
|
|
117
113
|
|
|
118
|
-
// ═══ Save
|
|
114
|
+
// ═══ Save ═══
|
|
119
115
|
console.log();
|
|
120
116
|
console.log(chalk.cyan(' ════════════════════════════════════'));
|
|
121
117
|
console.log(chalk.cyan(' 🦑 ' + agentFinalName + ' is hatched!'));
|
|
@@ -127,7 +123,6 @@ export async function hatchTUI(agentId) {
|
|
|
127
123
|
manifest.hatchedAt = new Date().toISOString();
|
|
128
124
|
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
129
125
|
|
|
130
|
-
// Update SOUL.md
|
|
131
126
|
const soulPath = join(agentDir, 'SOUL.md');
|
|
132
127
|
if (existsSync(soulPath)) {
|
|
133
128
|
let soul = readFileSync(soulPath, 'utf8');
|
|
@@ -137,36 +132,117 @@ export async function hatchTUI(agentId) {
|
|
|
137
132
|
|
|
138
133
|
writeFileSync(join(agentDir, 'IDENTITY.md'), '# ' + agentFinalName + '\n- Name: ' + agentFinalName + '\n- Hatched: ' + new Date().toISOString() + '\n- Emoji: 🦑\n');
|
|
139
134
|
|
|
140
|
-
console.log(chalk.green(' ✅ ' + agentFinalName + ' is
|
|
141
|
-
console.log(' ' + chalk.bold('
|
|
142
|
-
console.log(' ' + chalk.cyan('squidclaw start') + ' —
|
|
135
|
+
console.log(chalk.green(' ✅ ' + agentFinalName + ' is alive!\n'));
|
|
136
|
+
console.log(' ' + chalk.bold('Now run:\n'));
|
|
137
|
+
console.log(' ' + chalk.cyan('squidclaw start') + ' — launch the engine');
|
|
143
138
|
console.log(' ' + chalk.cyan('squidclaw tui') + ' — chat in terminal');
|
|
144
|
-
console.log(' ' + chalk.cyan('squidclaw status') + ' — check status');
|
|
145
139
|
console.log();
|
|
146
140
|
|
|
147
141
|
rl.close();
|
|
148
|
-
if (engine) {
|
|
149
|
-
try { await engine.stop(); } catch {}
|
|
150
|
-
}
|
|
142
|
+
if (engine) { try { await engine.stop(); } catch {} }
|
|
151
143
|
process.exit(0);
|
|
152
144
|
}
|
|
153
145
|
|
|
154
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Call AI directly (bypass engine API) for the hatch conversation
|
|
148
|
+
*/
|
|
149
|
+
async function callAgentDirect(config, manifest, message, history, hint) {
|
|
150
|
+
const provider = config.ai?.defaultProvider || 'anthropic';
|
|
151
|
+
const rawModel = manifest.model || config.ai?.defaultModel || 'claude-sonnet-4';
|
|
152
|
+
const model = MODEL_MAP[rawModel] || rawModel;
|
|
153
|
+
const providerConfig = config.ai?.providers?.[provider] || {};
|
|
154
|
+
const apiKey = providerConfig.key;
|
|
155
|
+
|
|
156
|
+
if (!apiKey || apiKey === 'local') {
|
|
157
|
+
return "I can't think yet — no API key configured. Check your config!";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const systemPrompt = 'You are ' + manifest.name + ', an AI agent that just hatched (came to life). This is your first conversation ever. Be playful, curious, excited. Keep responses SHORT (2-3 sentences max). No ---SPLIT--- markers. No "As an AI" phrases.' + (hint ? '\n\n' + hint : '');
|
|
161
|
+
|
|
162
|
+
const messages = [
|
|
163
|
+
{ role: 'user', content: message }
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
// Add history if available
|
|
167
|
+
if (history && history.length > 0) {
|
|
168
|
+
messages.length = 0;
|
|
169
|
+
for (const h of history.slice(-10)) {
|
|
170
|
+
messages.push({ role: h.role, content: h.content });
|
|
171
|
+
}
|
|
172
|
+
messages.push({ role: 'user', content: message });
|
|
173
|
+
}
|
|
174
|
+
|
|
155
175
|
try {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
176
|
+
// Determine API endpoint
|
|
177
|
+
let url, headers, body;
|
|
178
|
+
|
|
179
|
+
if (provider === 'anthropic') {
|
|
180
|
+
url = 'https://api.anthropic.com/v1/messages';
|
|
181
|
+
headers = {
|
|
182
|
+
'content-type': 'application/json',
|
|
183
|
+
'x-api-key': apiKey,
|
|
184
|
+
'anthropic-version': '2023-06-01',
|
|
185
|
+
};
|
|
186
|
+
// OAuth token support
|
|
187
|
+
if (apiKey.includes('sk-ant-oat')) {
|
|
188
|
+
headers['authorization'] = 'Bearer ' + apiKey;
|
|
189
|
+
headers['anthropic-beta'] = 'claude-code-20250219,oauth-2025-04-20';
|
|
190
|
+
delete headers['x-api-key'];
|
|
191
|
+
}
|
|
192
|
+
body = JSON.stringify({
|
|
193
|
+
model: model,
|
|
194
|
+
max_tokens: 300,
|
|
195
|
+
system: systemPrompt,
|
|
196
|
+
messages: messages,
|
|
197
|
+
});
|
|
198
|
+
} else if (provider === 'openai' || provider === 'groq' || provider === 'together' || provider === 'cerebras' || provider === 'mistral') {
|
|
199
|
+
const urls = {
|
|
200
|
+
openai: 'https://api.openai.com/v1/chat/completions',
|
|
201
|
+
groq: 'https://api.groq.com/openai/v1/chat/completions',
|
|
202
|
+
together: 'https://api.together.xyz/v1/chat/completions',
|
|
203
|
+
cerebras: 'https://api.cerebras.ai/v1/chat/completions',
|
|
204
|
+
mistral: 'https://api.mistral.ai/v1/chat/completions',
|
|
205
|
+
};
|
|
206
|
+
url = urls[provider];
|
|
207
|
+
headers = {
|
|
208
|
+
'content-type': 'application/json',
|
|
209
|
+
'authorization': 'Bearer ' + apiKey,
|
|
210
|
+
};
|
|
211
|
+
body = JSON.stringify({
|
|
212
|
+
model: model.replace(provider + '/', ''),
|
|
213
|
+
max_tokens: 300,
|
|
214
|
+
messages: [{ role: 'system', content: systemPrompt }, ...messages],
|
|
215
|
+
});
|
|
216
|
+
} else if (provider === 'google') {
|
|
217
|
+
const cleanModel = model.replace('google/', '');
|
|
218
|
+
url = 'https://generativelanguage.googleapis.com/v1beta/models/' + cleanModel + ':generateContent?key=' + apiKey;
|
|
219
|
+
headers = { 'content-type': 'application/json' };
|
|
220
|
+
body = JSON.stringify({
|
|
221
|
+
systemInstruction: { parts: [{ text: systemPrompt }] },
|
|
222
|
+
contents: messages.map(m => ({ role: m.role === 'assistant' ? 'model' : 'user', parts: [{ text: m.content }] })),
|
|
223
|
+
});
|
|
224
|
+
} else {
|
|
225
|
+
return "Provider " + provider + " isn't supported in hatch mode yet. Try starting the engine first.";
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const res = await fetch(url, { method: 'POST', headers, body });
|
|
165
229
|
const data = await res.json();
|
|
166
|
-
|
|
167
|
-
|
|
230
|
+
|
|
231
|
+
if (!res.ok) {
|
|
232
|
+
const errMsg = data?.error?.message || data?.message || JSON.stringify(data).slice(0, 200);
|
|
233
|
+
return '(AI error: ' + errMsg + ')';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Extract response based on provider
|
|
237
|
+
if (provider === 'anthropic') {
|
|
238
|
+
return data.content?.[0]?.text || '...';
|
|
239
|
+
} else if (provider === 'google') {
|
|
240
|
+
return data.candidates?.[0]?.content?.parts?.[0]?.text || '...';
|
|
241
|
+
} else {
|
|
242
|
+
return data.choices?.[0]?.message?.content || '...';
|
|
243
|
+
}
|
|
168
244
|
} catch (err) {
|
|
169
|
-
return '(
|
|
245
|
+
return '(connection error: ' + err.message + ')';
|
|
170
246
|
}
|
|
171
247
|
}
|
|
172
248
|
|
package/lib/cli/wa-login.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Shows QR code or pairing code right in the terminal
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { makeWASocket, useMultiFileAuthState, DisconnectReason, fetchLatestBaileysVersion } from '@whiskeysockets/baileys';
|
|
7
7
|
import qrcode from 'qrcode-terminal';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { mkdirSync } from 'fs';
|