fabiana 1.5.0 ā 1.7.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/dist/channels/telegram.js +32 -0
- package/dist/cli.js +5 -0
- package/dist/daemon/index.js +38 -1
- package/dist/paths.js +2 -0
- package/dist/plugins/gemini-image/index.js +42187 -0
- package/dist/plugins/gemini-image/package.json +9 -0
- package/dist/plugins/gemini-image/plugin.json +13 -0
- package/dist/plugins/stable-diffusion/index.js +2721 -0
- package/dist/plugins/stable-diffusion/package.json +9 -0
- package/dist/plugins/stable-diffusion/plugin.json +13 -0
- package/dist/plugins/tts/index.js +2701 -0
- package/dist/plugins/tts/package.json +9 -0
- package/dist/plugins/tts/plugin.json +13 -0
- package/dist/sync-cmd.js +89 -0
- package/dist/telegram/poller.js +4 -1
- package/package.json +1 -1
|
@@ -29,6 +29,38 @@ export class TelegramAdapter {
|
|
|
29
29
|
});
|
|
30
30
|
console.log(`šØ [telegram] Message queued: "${text.slice(0, 50)}"`);
|
|
31
31
|
});
|
|
32
|
+
this.bot.on('photo', async (ctx) => {
|
|
33
|
+
if (ctx.chat.id !== this.chatId)
|
|
34
|
+
return;
|
|
35
|
+
// Pick the largest available size (last in array)
|
|
36
|
+
const photo = ctx.message.photo[ctx.message.photo.length - 1];
|
|
37
|
+
const caption = ctx.message.caption ?? '';
|
|
38
|
+
try {
|
|
39
|
+
const fileLink = await this.bot.telegram.getFileLink(photo.file_id);
|
|
40
|
+
const response = await fetch(fileLink.href);
|
|
41
|
+
if (!response.ok)
|
|
42
|
+
throw new Error(`HTTP ${response.status}`);
|
|
43
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
44
|
+
await fs.mkdir(paths.imagesDir, { recursive: true });
|
|
45
|
+
const filename = `photo-${Date.now()}.jpg`;
|
|
46
|
+
const imagePath = paths.images(filename);
|
|
47
|
+
await fs.writeFile(imagePath, buffer);
|
|
48
|
+
const text = caption || '[sent a photo]';
|
|
49
|
+
this.queue.push({
|
|
50
|
+
text,
|
|
51
|
+
senderId: String(ctx.from.id),
|
|
52
|
+
channelId: String(ctx.chat.id),
|
|
53
|
+
threadId: String(ctx.message.message_id),
|
|
54
|
+
timestamp: new Date(ctx.message.date * 1000),
|
|
55
|
+
source: 'telegram',
|
|
56
|
+
imagePaths: [imagePath],
|
|
57
|
+
});
|
|
58
|
+
console.log(`šØ [telegram] Photo queued ā ${imagePath}`);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
console.error('ā Failed to download Telegram photo:', err.message);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
32
64
|
this.bot.catch((err) => {
|
|
33
65
|
console.error('Telegram error:', err);
|
|
34
66
|
});
|
package/dist/cli.js
CHANGED
|
@@ -16,6 +16,7 @@ import { runBackup, runRestore } from './backup.js';
|
|
|
16
16
|
import { pluginsAdd, pluginsList } from './plugins-cmd.js';
|
|
17
17
|
import { skillsAdd, skillsList, skillsRemove, skillsEnable, skillsDisable } from './skills-cmd.js';
|
|
18
18
|
import { runSetup } from './setup/index.js';
|
|
19
|
+
import { runSync } from './sync-cmd.js';
|
|
19
20
|
import { providerStatus, providerUse, providerAdd } from './provider-cmd.js';
|
|
20
21
|
import { modelStatus, modelUse, modelTest } from './model-cmd.js';
|
|
21
22
|
const C = '\x1b[96m'; // cyan ā name
|
|
@@ -49,6 +50,10 @@ program
|
|
|
49
50
|
.command('init')
|
|
50
51
|
.description('First time? Let\'s get acquainted')
|
|
51
52
|
.action(runSetup);
|
|
53
|
+
program
|
|
54
|
+
.command('sync')
|
|
55
|
+
.description('Sync bundled plugins and skills to ~/.fabiana (run after upgrades)')
|
|
56
|
+
.action(runSync);
|
|
52
57
|
program
|
|
53
58
|
.command('start')
|
|
54
59
|
.description('Wake her up ā she\'ll take it from there')
|
package/dist/daemon/index.js
CHANGED
|
@@ -158,6 +158,24 @@ export async function runPiSession(mode, incomingMessage, channel, incomingMsg,
|
|
|
158
158
|
await updateLastSolitude();
|
|
159
159
|
}
|
|
160
160
|
};
|
|
161
|
+
// Lightweight status sender ā chat mode only, no logging, no interaction tracking
|
|
162
|
+
const sendStatus = async (text) => {
|
|
163
|
+
if (mode === 'chat' && channel) {
|
|
164
|
+
await channel.send(text, incomingMsg?.channelId, incomingMsg?.threadId);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
|
168
|
+
const TOOL_STATUS = {
|
|
169
|
+
safe_read: ['having a look...', 'let me check that.', 'nosing through your files...', 'one sec, reading.'],
|
|
170
|
+
safe_write: ['writing that down...', 'putting that somewhere safe.', 'noted, filing it away.'],
|
|
171
|
+
safe_edit: ['making a small edit...', 'adjusting things a bit.', 'tweaking that.'],
|
|
172
|
+
fetch_url: ['heading out to the web...', 'brb, checking the internet.', 'let me go look that up.'],
|
|
173
|
+
brave_search: ['diving into search...', 'looking that up for you.', 'let me see what I can find.'],
|
|
174
|
+
hackernews: ['peeking at hacker news...', 'seeing what the nerds are saying.', 'checking HN real quick.'],
|
|
175
|
+
calendar: ['checking your schedule...', 'having a look at your calendar.', 'let me see what\'s coming up.'],
|
|
176
|
+
manage_todo: ['peeking at your todos...', 'checking what\'s on your list.'],
|
|
177
|
+
bash: ['running something for you...', 'brb, executing.', 'let me run that.'],
|
|
178
|
+
};
|
|
161
179
|
console.log('[6/8] Creating tools...');
|
|
162
180
|
const fabianaTools = createFabianaTools(permissions, sendMessage, {
|
|
163
181
|
toolset,
|
|
@@ -199,6 +217,11 @@ export async function runPiSession(mode, incomingMessage, channel, incomingMsg,
|
|
|
199
217
|
if (event.toolName === 'send_message') {
|
|
200
218
|
sendMessageCalled = true;
|
|
201
219
|
}
|
|
220
|
+
else {
|
|
221
|
+
const variants = TOOL_STATUS[event.toolName];
|
|
222
|
+
if (variants)
|
|
223
|
+
await sendStatus(pick(variants));
|
|
224
|
+
}
|
|
202
225
|
lastActivityWasThinking = false;
|
|
203
226
|
}
|
|
204
227
|
if (event.type === 'tool_execution_end') {
|
|
@@ -221,7 +244,21 @@ export async function runPiSession(mode, incomingMessage, channel, incomingMsg,
|
|
|
221
244
|
const prompt = buildPrompt(context);
|
|
222
245
|
console.log(` Context loaded: ${prompt.length} chars`);
|
|
223
246
|
console.log('\nš Sending prompt to agent...');
|
|
224
|
-
|
|
247
|
+
if (mode === 'chat') {
|
|
248
|
+
await sendStatus(pick(['on it.', 'give me a sec.', 'let me think.', 'sure, one moment.', 'on it!']));
|
|
249
|
+
}
|
|
250
|
+
// Load any attached images as base64 for the Pi SDK
|
|
251
|
+
let promptImages;
|
|
252
|
+
if (incomingMsg?.imagePaths?.length) {
|
|
253
|
+
promptImages = await Promise.all(incomingMsg.imagePaths.map(async (p) => {
|
|
254
|
+
const data = await fs.readFile(p);
|
|
255
|
+
const ext = p.split('.').pop()?.toLowerCase() ?? 'jpg';
|
|
256
|
+
const mimeType = ext === 'png' ? 'image/png' : ext === 'gif' ? 'image/gif' : ext === 'webp' ? 'image/webp' : 'image/jpeg';
|
|
257
|
+
return { type: 'image', data: data.toString('base64'), mimeType };
|
|
258
|
+
}));
|
|
259
|
+
console.log(` š¼ļø Attaching ${promptImages.length} image(s) to prompt`);
|
|
260
|
+
}
|
|
261
|
+
await session.prompt(prompt, promptImages ? { images: promptImages } : undefined);
|
|
225
262
|
console.log('\nā³ Waiting for agent to complete...');
|
|
226
263
|
await session.agent.waitForIdle();
|
|
227
264
|
if (lastActivityWasThinking) {
|
package/dist/paths.js
CHANGED
|
@@ -18,6 +18,8 @@ export const paths = {
|
|
|
18
18
|
logs: (filename) => join(DATA_DIR, 'logs', filename),
|
|
19
19
|
sessions: join(DATA_DIR, 'sessions'),
|
|
20
20
|
agentTodo: join(DATA_DIR, 'agent-todo'),
|
|
21
|
+
imagesDir: join(DATA_DIR, 'images'),
|
|
22
|
+
images: (filename) => join(DATA_DIR, 'images', filename),
|
|
21
23
|
conversations: join(DATA_DIR, 'conversations'),
|
|
22
24
|
moodMd: join(DATA_DIR, 'memory', 'mood.md'),
|
|
23
25
|
memoryDb: join(DATA_DIR, 'memory.db'),
|