@madh-io/alfred-ai 0.7.2 → 0.8.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/bundle/index.js +2223 -123
- package/package.json +5 -2
package/bundle/index.js
CHANGED
|
@@ -11,7 +11,7 @@ var __export = (target, all) => {
|
|
|
11
11
|
|
|
12
12
|
// ../config/dist/schema.js
|
|
13
13
|
import { z } from "zod";
|
|
14
|
-
var TelegramConfigSchema, DiscordConfigSchema, WhatsAppConfigSchema, MatrixConfigSchema, SignalConfigSchema, StorageConfigSchema, LoggerConfigSchema, SecurityConfigSchema, LLMProviderConfigSchema, SearchConfigSchema, EmailConfigSchema, SpeechConfigSchema, CalDAVConfigSchema, GoogleCalendarConfigSchema, MicrosoftCalendarConfigSchema, CalendarConfigSchema, AlfredConfigSchema;
|
|
14
|
+
var TelegramConfigSchema, DiscordConfigSchema, WhatsAppConfigSchema, MatrixConfigSchema, SignalConfigSchema, StorageConfigSchema, LoggerConfigSchema, SecurityConfigSchema, LLMProviderConfigSchema, SearchConfigSchema, EmailConfigSchema, SpeechConfigSchema, CalDAVConfigSchema, GoogleCalendarConfigSchema, MicrosoftCalendarConfigSchema, CalendarConfigSchema, MCPServerConfigSchema, MCPConfigSchema, CodeSandboxConfigSchema, AlfredConfigSchema;
|
|
15
15
|
var init_schema = __esm({
|
|
16
16
|
"../config/dist/schema.js"() {
|
|
17
17
|
"use strict";
|
|
@@ -107,6 +107,22 @@ var init_schema = __esm({
|
|
|
107
107
|
google: GoogleCalendarConfigSchema.optional(),
|
|
108
108
|
microsoft: MicrosoftCalendarConfigSchema.optional()
|
|
109
109
|
});
|
|
110
|
+
MCPServerConfigSchema = z.object({
|
|
111
|
+
name: z.string(),
|
|
112
|
+
command: z.string().optional(),
|
|
113
|
+
args: z.array(z.string()).optional(),
|
|
114
|
+
env: z.record(z.string()).optional(),
|
|
115
|
+
url: z.string().optional()
|
|
116
|
+
});
|
|
117
|
+
MCPConfigSchema = z.object({
|
|
118
|
+
servers: z.array(MCPServerConfigSchema)
|
|
119
|
+
});
|
|
120
|
+
CodeSandboxConfigSchema = z.object({
|
|
121
|
+
enabled: z.boolean(),
|
|
122
|
+
allowedLanguages: z.array(z.enum(["javascript", "python"])).optional(),
|
|
123
|
+
maxTimeoutMs: z.number().optional(),
|
|
124
|
+
allowNetwork: z.boolean().optional()
|
|
125
|
+
});
|
|
110
126
|
AlfredConfigSchema = z.object({
|
|
111
127
|
name: z.string(),
|
|
112
128
|
telegram: TelegramConfigSchema,
|
|
@@ -121,7 +137,9 @@ var init_schema = __esm({
|
|
|
121
137
|
search: SearchConfigSchema.optional(),
|
|
122
138
|
email: EmailConfigSchema.optional(),
|
|
123
139
|
speech: SpeechConfigSchema.optional(),
|
|
124
|
-
calendar: CalendarConfigSchema.optional()
|
|
140
|
+
calendar: CalendarConfigSchema.optional(),
|
|
141
|
+
mcp: MCPConfigSchema.optional(),
|
|
142
|
+
codeSandbox: CodeSandboxConfigSchema.optional()
|
|
125
143
|
});
|
|
126
144
|
}
|
|
127
145
|
});
|
|
@@ -502,6 +520,100 @@ var init_migrations = __esm({
|
|
|
502
520
|
ON embeddings(source_type, source_id);
|
|
503
521
|
`);
|
|
504
522
|
}
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
version: 7,
|
|
526
|
+
description: "Background tasks table",
|
|
527
|
+
up(db) {
|
|
528
|
+
db.exec(`
|
|
529
|
+
CREATE TABLE IF NOT EXISTS background_tasks (
|
|
530
|
+
id TEXT PRIMARY KEY,
|
|
531
|
+
user_id TEXT NOT NULL,
|
|
532
|
+
platform TEXT NOT NULL,
|
|
533
|
+
chat_id TEXT NOT NULL,
|
|
534
|
+
description TEXT NOT NULL,
|
|
535
|
+
skill_name TEXT NOT NULL,
|
|
536
|
+
skill_input TEXT NOT NULL,
|
|
537
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
538
|
+
result TEXT,
|
|
539
|
+
error TEXT,
|
|
540
|
+
created_at TEXT NOT NULL,
|
|
541
|
+
started_at TEXT,
|
|
542
|
+
completed_at TEXT
|
|
543
|
+
);
|
|
544
|
+
`);
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
version: 8,
|
|
549
|
+
description: "Scheduled actions for proactive behavior",
|
|
550
|
+
up(db) {
|
|
551
|
+
db.exec(`
|
|
552
|
+
CREATE TABLE IF NOT EXISTS scheduled_actions (
|
|
553
|
+
id TEXT PRIMARY KEY,
|
|
554
|
+
user_id TEXT NOT NULL,
|
|
555
|
+
platform TEXT NOT NULL,
|
|
556
|
+
chat_id TEXT NOT NULL,
|
|
557
|
+
name TEXT NOT NULL,
|
|
558
|
+
description TEXT NOT NULL,
|
|
559
|
+
schedule_type TEXT NOT NULL,
|
|
560
|
+
schedule_value TEXT NOT NULL,
|
|
561
|
+
skill_name TEXT NOT NULL,
|
|
562
|
+
skill_input TEXT NOT NULL,
|
|
563
|
+
prompt_template TEXT,
|
|
564
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
565
|
+
last_run_at TEXT,
|
|
566
|
+
next_run_at TEXT,
|
|
567
|
+
created_at TEXT NOT NULL
|
|
568
|
+
);
|
|
569
|
+
`);
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
version: 9,
|
|
574
|
+
description: "Cross-platform user linking",
|
|
575
|
+
up(db) {
|
|
576
|
+
db.exec(`
|
|
577
|
+
ALTER TABLE users ADD COLUMN master_user_id TEXT REFERENCES users(id);
|
|
578
|
+
|
|
579
|
+
CREATE TABLE IF NOT EXISTS link_tokens (
|
|
580
|
+
id TEXT PRIMARY KEY,
|
|
581
|
+
code TEXT NOT NULL UNIQUE,
|
|
582
|
+
user_id TEXT NOT NULL,
|
|
583
|
+
platform TEXT NOT NULL,
|
|
584
|
+
created_at TEXT NOT NULL,
|
|
585
|
+
expires_at TEXT NOT NULL
|
|
586
|
+
);
|
|
587
|
+
CREATE INDEX IF NOT EXISTS idx_link_tokens_code ON link_tokens(code);
|
|
588
|
+
`);
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
version: 10,
|
|
593
|
+
description: "Document intelligence tables",
|
|
594
|
+
up(db) {
|
|
595
|
+
db.exec(`
|
|
596
|
+
CREATE TABLE IF NOT EXISTS documents (
|
|
597
|
+
id TEXT PRIMARY KEY,
|
|
598
|
+
user_id TEXT NOT NULL,
|
|
599
|
+
filename TEXT NOT NULL,
|
|
600
|
+
mime_type TEXT NOT NULL,
|
|
601
|
+
size_bytes INTEGER NOT NULL,
|
|
602
|
+
chunk_count INTEGER NOT NULL DEFAULT 0,
|
|
603
|
+
created_at TEXT NOT NULL
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
CREATE TABLE IF NOT EXISTS document_chunks (
|
|
607
|
+
id TEXT PRIMARY KEY,
|
|
608
|
+
document_id TEXT NOT NULL REFERENCES documents(id),
|
|
609
|
+
chunk_index INTEGER NOT NULL,
|
|
610
|
+
content TEXT NOT NULL,
|
|
611
|
+
embedding_id TEXT REFERENCES embeddings(id),
|
|
612
|
+
created_at TEXT NOT NULL
|
|
613
|
+
);
|
|
614
|
+
CREATE INDEX IF NOT EXISTS idx_doc_chunks_doc ON document_chunks(document_id);
|
|
615
|
+
`);
|
|
616
|
+
}
|
|
505
617
|
}
|
|
506
618
|
];
|
|
507
619
|
}
|
|
@@ -770,6 +882,17 @@ var init_user_repository = __esm({
|
|
|
770
882
|
preferences: row.preferences ? JSON.parse(row.preferences) : void 0
|
|
771
883
|
};
|
|
772
884
|
}
|
|
885
|
+
setMasterUser(userId, masterUserId) {
|
|
886
|
+
this.db.prepare("UPDATE users SET master_user_id = ?, updated_at = ? WHERE id = ?").run(masterUserId, (/* @__PURE__ */ new Date()).toISOString(), userId);
|
|
887
|
+
}
|
|
888
|
+
getLinkedUsers(masterUserId) {
|
|
889
|
+
const rows = this.db.prepare("SELECT * FROM users WHERE master_user_id = ? OR id = ?").all(masterUserId, masterUserId);
|
|
890
|
+
return rows.map((r) => this.mapRow(r));
|
|
891
|
+
}
|
|
892
|
+
getMasterUserId(userId) {
|
|
893
|
+
const row = this.db.prepare("SELECT master_user_id FROM users WHERE id = ?").get(userId);
|
|
894
|
+
return row?.master_user_id ?? userId;
|
|
895
|
+
}
|
|
773
896
|
mapRow(row) {
|
|
774
897
|
return {
|
|
775
898
|
id: row.id,
|
|
@@ -781,6 +904,7 @@ var init_user_repository = __esm({
|
|
|
781
904
|
language: row.language ?? void 0,
|
|
782
905
|
bio: row.bio ?? void 0,
|
|
783
906
|
preferences: row.preferences ? JSON.parse(row.preferences) : void 0,
|
|
907
|
+
masterUserId: row.master_user_id ?? void 0,
|
|
784
908
|
createdAt: row.created_at,
|
|
785
909
|
updatedAt: row.updated_at
|
|
786
910
|
};
|
|
@@ -1112,6 +1236,367 @@ var init_embedding_repository = __esm({
|
|
|
1112
1236
|
}
|
|
1113
1237
|
});
|
|
1114
1238
|
|
|
1239
|
+
// ../storage/dist/repositories/link-token-repository.js
|
|
1240
|
+
import crypto3 from "node:crypto";
|
|
1241
|
+
var LinkTokenRepository;
|
|
1242
|
+
var init_link_token_repository = __esm({
|
|
1243
|
+
"../storage/dist/repositories/link-token-repository.js"() {
|
|
1244
|
+
"use strict";
|
|
1245
|
+
LinkTokenRepository = class {
|
|
1246
|
+
db;
|
|
1247
|
+
constructor(db) {
|
|
1248
|
+
this.db = db;
|
|
1249
|
+
}
|
|
1250
|
+
create(userId, platform) {
|
|
1251
|
+
const token = {
|
|
1252
|
+
id: crypto3.randomUUID(),
|
|
1253
|
+
code: String(Math.floor(1e5 + Math.random() * 9e5)),
|
|
1254
|
+
// 6-digit
|
|
1255
|
+
userId,
|
|
1256
|
+
platform,
|
|
1257
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1258
|
+
expiresAt: new Date(Date.now() + 10 * 60 * 1e3).toISOString()
|
|
1259
|
+
// 10 min
|
|
1260
|
+
};
|
|
1261
|
+
this.db.prepare(`
|
|
1262
|
+
INSERT INTO link_tokens (id, code, user_id, platform, created_at, expires_at)
|
|
1263
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
1264
|
+
`).run(token.id, token.code, token.userId, token.platform, token.createdAt, token.expiresAt);
|
|
1265
|
+
return token;
|
|
1266
|
+
}
|
|
1267
|
+
findByCode(code) {
|
|
1268
|
+
const row = this.db.prepare("SELECT * FROM link_tokens WHERE code = ? AND expires_at > ?").get(code, (/* @__PURE__ */ new Date()).toISOString());
|
|
1269
|
+
if (!row)
|
|
1270
|
+
return void 0;
|
|
1271
|
+
return {
|
|
1272
|
+
id: row.id,
|
|
1273
|
+
code: row.code,
|
|
1274
|
+
userId: row.user_id,
|
|
1275
|
+
platform: row.platform,
|
|
1276
|
+
createdAt: row.created_at,
|
|
1277
|
+
expiresAt: row.expires_at
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
consume(id) {
|
|
1281
|
+
this.db.prepare("DELETE FROM link_tokens WHERE id = ?").run(id);
|
|
1282
|
+
}
|
|
1283
|
+
cleanup() {
|
|
1284
|
+
this.db.prepare("DELETE FROM link_tokens WHERE expires_at <= ?").run((/* @__PURE__ */ new Date()).toISOString());
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
// ../storage/dist/repositories/background-task-repository.js
|
|
1291
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
1292
|
+
var BackgroundTaskRepository;
|
|
1293
|
+
var init_background_task_repository = __esm({
|
|
1294
|
+
"../storage/dist/repositories/background-task-repository.js"() {
|
|
1295
|
+
"use strict";
|
|
1296
|
+
BackgroundTaskRepository = class {
|
|
1297
|
+
db;
|
|
1298
|
+
constructor(db) {
|
|
1299
|
+
this.db = db;
|
|
1300
|
+
}
|
|
1301
|
+
create(userId, platform, chatId, description, skillName, skillInput) {
|
|
1302
|
+
const task = {
|
|
1303
|
+
id: randomUUID5(),
|
|
1304
|
+
userId,
|
|
1305
|
+
platform,
|
|
1306
|
+
chatId,
|
|
1307
|
+
description,
|
|
1308
|
+
skillName,
|
|
1309
|
+
skillInput,
|
|
1310
|
+
status: "pending",
|
|
1311
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1312
|
+
};
|
|
1313
|
+
this.db.prepare(`
|
|
1314
|
+
INSERT INTO background_tasks (id, user_id, platform, chat_id, description, skill_name, skill_input, status, created_at)
|
|
1315
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1316
|
+
`).run(task.id, task.userId, task.platform, task.chatId, task.description, task.skillName, task.skillInput, task.status, task.createdAt);
|
|
1317
|
+
return task;
|
|
1318
|
+
}
|
|
1319
|
+
updateStatus(id, status, result, error) {
|
|
1320
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1321
|
+
let startedAt = null;
|
|
1322
|
+
let completedAt = null;
|
|
1323
|
+
if (status === "running") {
|
|
1324
|
+
startedAt = now;
|
|
1325
|
+
}
|
|
1326
|
+
if (status === "completed" || status === "failed") {
|
|
1327
|
+
completedAt = now;
|
|
1328
|
+
}
|
|
1329
|
+
this.db.prepare(`
|
|
1330
|
+
UPDATE background_tasks
|
|
1331
|
+
SET status = ?,
|
|
1332
|
+
result = COALESCE(?, result),
|
|
1333
|
+
error = COALESCE(?, error),
|
|
1334
|
+
started_at = COALESCE(?, started_at),
|
|
1335
|
+
completed_at = COALESCE(?, completed_at)
|
|
1336
|
+
WHERE id = ?
|
|
1337
|
+
`).run(status, result ?? null, error ?? null, startedAt, completedAt, id);
|
|
1338
|
+
}
|
|
1339
|
+
getPending(limit = 10) {
|
|
1340
|
+
const rows = this.db.prepare(`SELECT * FROM background_tasks WHERE status = 'pending' ORDER BY created_at ASC LIMIT ?`).all(limit);
|
|
1341
|
+
return rows.map((row) => this.mapRow(row));
|
|
1342
|
+
}
|
|
1343
|
+
getByUser(userId) {
|
|
1344
|
+
const rows = this.db.prepare(`SELECT * FROM background_tasks
|
|
1345
|
+
WHERE user_id = ?
|
|
1346
|
+
AND (status IN ('pending', 'running') OR completed_at > datetime('now', '-1 day'))
|
|
1347
|
+
ORDER BY created_at DESC`).all(userId);
|
|
1348
|
+
return rows.map((row) => this.mapRow(row));
|
|
1349
|
+
}
|
|
1350
|
+
cancel(id) {
|
|
1351
|
+
const result = this.db.prepare(`DELETE FROM background_tasks WHERE id = ? AND status IN ('pending', 'running')`).run(id);
|
|
1352
|
+
return result.changes > 0;
|
|
1353
|
+
}
|
|
1354
|
+
cleanup(olderThanDays = 7) {
|
|
1355
|
+
const result = this.db.prepare(`DELETE FROM background_tasks
|
|
1356
|
+
WHERE status IN ('completed', 'failed')
|
|
1357
|
+
AND completed_at < datetime('now', '-' || ? || ' days')`).run(olderThanDays);
|
|
1358
|
+
return result.changes;
|
|
1359
|
+
}
|
|
1360
|
+
mapRow(row) {
|
|
1361
|
+
return {
|
|
1362
|
+
id: row.id,
|
|
1363
|
+
userId: row.user_id,
|
|
1364
|
+
platform: row.platform,
|
|
1365
|
+
chatId: row.chat_id,
|
|
1366
|
+
description: row.description,
|
|
1367
|
+
skillName: row.skill_name,
|
|
1368
|
+
skillInput: row.skill_input,
|
|
1369
|
+
status: row.status,
|
|
1370
|
+
result: row.result,
|
|
1371
|
+
error: row.error,
|
|
1372
|
+
createdAt: row.created_at,
|
|
1373
|
+
startedAt: row.started_at,
|
|
1374
|
+
completedAt: row.completed_at
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
// ../storage/dist/repositories/scheduled-action-repository.js
|
|
1382
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
1383
|
+
var ScheduledActionRepository;
|
|
1384
|
+
var init_scheduled_action_repository = __esm({
|
|
1385
|
+
"../storage/dist/repositories/scheduled-action-repository.js"() {
|
|
1386
|
+
"use strict";
|
|
1387
|
+
ScheduledActionRepository = class {
|
|
1388
|
+
db;
|
|
1389
|
+
constructor(db) {
|
|
1390
|
+
this.db = db;
|
|
1391
|
+
}
|
|
1392
|
+
create(data) {
|
|
1393
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1394
|
+
const id = randomUUID6();
|
|
1395
|
+
const nextRunAt = this.calculateInitialNextRun(data.scheduleType, data.scheduleValue);
|
|
1396
|
+
this.db.prepare(`
|
|
1397
|
+
INSERT INTO scheduled_actions
|
|
1398
|
+
(id, user_id, platform, chat_id, name, description, schedule_type, schedule_value,
|
|
1399
|
+
skill_name, skill_input, prompt_template, enabled, next_run_at, created_at)
|
|
1400
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)
|
|
1401
|
+
`).run(id, data.userId, data.platform, data.chatId, data.name, data.description, data.scheduleType, data.scheduleValue, data.skillName, data.skillInput, data.promptTemplate ?? null, nextRunAt, now);
|
|
1402
|
+
return {
|
|
1403
|
+
id,
|
|
1404
|
+
userId: data.userId,
|
|
1405
|
+
platform: data.platform,
|
|
1406
|
+
chatId: data.chatId,
|
|
1407
|
+
name: data.name,
|
|
1408
|
+
description: data.description,
|
|
1409
|
+
scheduleType: data.scheduleType,
|
|
1410
|
+
scheduleValue: data.scheduleValue,
|
|
1411
|
+
skillName: data.skillName,
|
|
1412
|
+
skillInput: data.skillInput,
|
|
1413
|
+
promptTemplate: data.promptTemplate,
|
|
1414
|
+
enabled: true,
|
|
1415
|
+
nextRunAt: nextRunAt ?? void 0,
|
|
1416
|
+
createdAt: now
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
findById(id) {
|
|
1420
|
+
const row = this.db.prepare(`SELECT * FROM scheduled_actions WHERE id = ?`).get(id);
|
|
1421
|
+
return row ? this.mapRow(row) : void 0;
|
|
1422
|
+
}
|
|
1423
|
+
getByUser(userId) {
|
|
1424
|
+
const rows = this.db.prepare(`SELECT * FROM scheduled_actions WHERE user_id = ? ORDER BY created_at DESC`).all(userId);
|
|
1425
|
+
return rows.map((row) => this.mapRow(row));
|
|
1426
|
+
}
|
|
1427
|
+
getDue() {
|
|
1428
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1429
|
+
const rows = this.db.prepare(`SELECT * FROM scheduled_actions
|
|
1430
|
+
WHERE enabled = 1 AND next_run_at IS NOT NULL AND next_run_at <= ?
|
|
1431
|
+
ORDER BY next_run_at ASC`).all(now);
|
|
1432
|
+
return rows.map((row) => this.mapRow(row));
|
|
1433
|
+
}
|
|
1434
|
+
updateLastRun(id, lastRunAt, nextRunAt) {
|
|
1435
|
+
this.db.prepare(`
|
|
1436
|
+
UPDATE scheduled_actions
|
|
1437
|
+
SET last_run_at = ?, next_run_at = ?
|
|
1438
|
+
WHERE id = ?
|
|
1439
|
+
`).run(lastRunAt, nextRunAt, id);
|
|
1440
|
+
}
|
|
1441
|
+
setEnabled(id, enabled) {
|
|
1442
|
+
const result = this.db.prepare(`UPDATE scheduled_actions SET enabled = ? WHERE id = ?`).run(enabled ? 1 : 0, id);
|
|
1443
|
+
return result.changes > 0;
|
|
1444
|
+
}
|
|
1445
|
+
delete(id) {
|
|
1446
|
+
const result = this.db.prepare(`DELETE FROM scheduled_actions WHERE id = ?`).run(id);
|
|
1447
|
+
return result.changes > 0;
|
|
1448
|
+
}
|
|
1449
|
+
calculateInitialNextRun(scheduleType, scheduleValue) {
|
|
1450
|
+
const now = /* @__PURE__ */ new Date();
|
|
1451
|
+
switch (scheduleType) {
|
|
1452
|
+
case "interval": {
|
|
1453
|
+
const minutes = parseInt(scheduleValue, 10);
|
|
1454
|
+
if (isNaN(minutes) || minutes <= 0)
|
|
1455
|
+
return null;
|
|
1456
|
+
return new Date(now.getTime() + minutes * 6e4).toISOString();
|
|
1457
|
+
}
|
|
1458
|
+
case "once": {
|
|
1459
|
+
return new Date(scheduleValue).toISOString();
|
|
1460
|
+
}
|
|
1461
|
+
case "cron": {
|
|
1462
|
+
return this.getNextCronDate(scheduleValue, now)?.toISOString() ?? null;
|
|
1463
|
+
}
|
|
1464
|
+
default:
|
|
1465
|
+
return null;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
getNextCronDate(cronExpr, after) {
|
|
1469
|
+
const parts = cronExpr.trim().split(/\s+/);
|
|
1470
|
+
if (parts.length !== 5)
|
|
1471
|
+
return null;
|
|
1472
|
+
const candidate = new Date(after.getTime() + 6e4);
|
|
1473
|
+
candidate.setSeconds(0, 0);
|
|
1474
|
+
for (let i = 0; i < 1440; i++) {
|
|
1475
|
+
if (this.matchesCron(parts, candidate)) {
|
|
1476
|
+
return candidate;
|
|
1477
|
+
}
|
|
1478
|
+
candidate.setTime(candidate.getTime() + 6e4);
|
|
1479
|
+
}
|
|
1480
|
+
return null;
|
|
1481
|
+
}
|
|
1482
|
+
matchesCron(parts, date) {
|
|
1483
|
+
const minute = date.getMinutes();
|
|
1484
|
+
const hour = date.getHours();
|
|
1485
|
+
const dayOfMonth = date.getDate();
|
|
1486
|
+
const month = date.getMonth() + 1;
|
|
1487
|
+
const dayOfWeek = date.getDay();
|
|
1488
|
+
return this.matchCronField(parts[0], minute) && this.matchCronField(parts[1], hour) && this.matchCronField(parts[2], dayOfMonth) && this.matchCronField(parts[3], month) && this.matchCronField(parts[4], dayOfWeek);
|
|
1489
|
+
}
|
|
1490
|
+
matchCronField(field, value) {
|
|
1491
|
+
if (field === "*")
|
|
1492
|
+
return true;
|
|
1493
|
+
const stepMatch = /^\*\/(\d+)$/.exec(field);
|
|
1494
|
+
if (stepMatch) {
|
|
1495
|
+
const step = parseInt(stepMatch[1], 10);
|
|
1496
|
+
return value % step === 0;
|
|
1497
|
+
}
|
|
1498
|
+
const num = parseInt(field, 10);
|
|
1499
|
+
if (!isNaN(num))
|
|
1500
|
+
return value === num;
|
|
1501
|
+
return false;
|
|
1502
|
+
}
|
|
1503
|
+
mapRow(row) {
|
|
1504
|
+
return {
|
|
1505
|
+
id: row.id,
|
|
1506
|
+
userId: row.user_id,
|
|
1507
|
+
platform: row.platform,
|
|
1508
|
+
chatId: row.chat_id,
|
|
1509
|
+
name: row.name,
|
|
1510
|
+
description: row.description,
|
|
1511
|
+
scheduleType: row.schedule_type,
|
|
1512
|
+
scheduleValue: row.schedule_value,
|
|
1513
|
+
skillName: row.skill_name,
|
|
1514
|
+
skillInput: row.skill_input,
|
|
1515
|
+
promptTemplate: row.prompt_template,
|
|
1516
|
+
enabled: row.enabled === 1,
|
|
1517
|
+
lastRunAt: row.last_run_at,
|
|
1518
|
+
nextRunAt: row.next_run_at,
|
|
1519
|
+
createdAt: row.created_at
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
});
|
|
1525
|
+
|
|
1526
|
+
// ../storage/dist/repositories/document-repository.js
|
|
1527
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
1528
|
+
var DocumentRepository;
|
|
1529
|
+
var init_document_repository = __esm({
|
|
1530
|
+
"../storage/dist/repositories/document-repository.js"() {
|
|
1531
|
+
"use strict";
|
|
1532
|
+
DocumentRepository = class {
|
|
1533
|
+
db;
|
|
1534
|
+
constructor(db) {
|
|
1535
|
+
this.db = db;
|
|
1536
|
+
}
|
|
1537
|
+
createDocument(userId, filename, mimeType, sizeBytes) {
|
|
1538
|
+
const id = randomUUID7();
|
|
1539
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1540
|
+
this.db.prepare("INSERT INTO documents (id, user_id, filename, mime_type, size_bytes, chunk_count, created_at) VALUES (?, ?, ?, ?, ?, 0, ?)").run(id, userId, filename, mimeType, sizeBytes, now);
|
|
1541
|
+
return { id, userId, filename, mimeType, sizeBytes, chunkCount: 0, createdAt: now };
|
|
1542
|
+
}
|
|
1543
|
+
updateChunkCount(documentId, count) {
|
|
1544
|
+
this.db.prepare("UPDATE documents SET chunk_count = ? WHERE id = ?").run(count, documentId);
|
|
1545
|
+
}
|
|
1546
|
+
addChunk(documentId, chunkIndex, content, embeddingId) {
|
|
1547
|
+
const id = randomUUID7();
|
|
1548
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1549
|
+
this.db.prepare("INSERT INTO document_chunks (id, document_id, chunk_index, content, embedding_id, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, documentId, chunkIndex, content, embeddingId ?? null, now);
|
|
1550
|
+
return { id, documentId, chunkIndex, content, embeddingId, createdAt: now };
|
|
1551
|
+
}
|
|
1552
|
+
getDocument(id) {
|
|
1553
|
+
const row = this.db.prepare("SELECT * FROM documents WHERE id = ?").get(id);
|
|
1554
|
+
return row ? this.mapDocumentRow(row) : void 0;
|
|
1555
|
+
}
|
|
1556
|
+
getChunks(documentId) {
|
|
1557
|
+
const rows = this.db.prepare("SELECT * FROM document_chunks WHERE document_id = ? ORDER BY chunk_index ASC").all(documentId);
|
|
1558
|
+
return rows.map((r) => this.mapChunkRow(r));
|
|
1559
|
+
}
|
|
1560
|
+
listByUser(userId) {
|
|
1561
|
+
const rows = this.db.prepare("SELECT * FROM documents WHERE user_id = ? ORDER BY created_at DESC").all(userId);
|
|
1562
|
+
return rows.map((r) => this.mapDocumentRow(r));
|
|
1563
|
+
}
|
|
1564
|
+
deleteDocument(id) {
|
|
1565
|
+
this.db.prepare("DELETE FROM document_chunks WHERE document_id = ?").run(id);
|
|
1566
|
+
this.db.prepare("DELETE FROM documents WHERE id = ?").run(id);
|
|
1567
|
+
}
|
|
1568
|
+
getChunksByEmbeddingIds(embeddingIds) {
|
|
1569
|
+
if (embeddingIds.length === 0)
|
|
1570
|
+
return [];
|
|
1571
|
+
const placeholders = embeddingIds.map(() => "?").join(", ");
|
|
1572
|
+
const rows = this.db.prepare(`SELECT * FROM document_chunks WHERE embedding_id IN (${placeholders}) ORDER BY chunk_index ASC`).all(...embeddingIds);
|
|
1573
|
+
return rows.map((r) => this.mapChunkRow(r));
|
|
1574
|
+
}
|
|
1575
|
+
mapDocumentRow(row) {
|
|
1576
|
+
return {
|
|
1577
|
+
id: row.id,
|
|
1578
|
+
userId: row.user_id,
|
|
1579
|
+
filename: row.filename,
|
|
1580
|
+
mimeType: row.mime_type,
|
|
1581
|
+
sizeBytes: row.size_bytes,
|
|
1582
|
+
chunkCount: row.chunk_count,
|
|
1583
|
+
createdAt: row.created_at
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
mapChunkRow(row) {
|
|
1587
|
+
return {
|
|
1588
|
+
id: row.id,
|
|
1589
|
+
documentId: row.document_id,
|
|
1590
|
+
chunkIndex: row.chunk_index,
|
|
1591
|
+
content: row.content,
|
|
1592
|
+
embeddingId: row.embedding_id || void 0,
|
|
1593
|
+
createdAt: row.created_at
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
});
|
|
1599
|
+
|
|
1115
1600
|
// ../storage/dist/index.js
|
|
1116
1601
|
var init_dist3 = __esm({
|
|
1117
1602
|
"../storage/dist/index.js"() {
|
|
@@ -1126,6 +1611,10 @@ var init_dist3 = __esm({
|
|
|
1126
1611
|
init_reminder_repository();
|
|
1127
1612
|
init_note_repository();
|
|
1128
1613
|
init_embedding_repository();
|
|
1614
|
+
init_link_token_repository();
|
|
1615
|
+
init_background_task_repository();
|
|
1616
|
+
init_scheduled_action_repository();
|
|
1617
|
+
init_document_repository();
|
|
1129
1618
|
}
|
|
1130
1619
|
});
|
|
1131
1620
|
|
|
@@ -2012,9 +2501,9 @@ var init_prompt_builder = __esm({
|
|
|
2012
2501
|
PromptBuilder = class {
|
|
2013
2502
|
buildSystemPrompt(context = {}) {
|
|
2014
2503
|
const { memories, skills, userProfile, todayEvents } = context;
|
|
2015
|
-
const
|
|
2504
|
+
const os4 = process.platform === "darwin" ? "macOS" : process.platform === "win32" ? "Windows" : "Linux";
|
|
2016
2505
|
const homeDir = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
2017
|
-
let prompt = `You are Alfred, a personal AI assistant. You run on ${
|
|
2506
|
+
let prompt = `You are Alfred, a personal AI assistant. You run on ${os4} (home: ${homeDir}).
|
|
2018
2507
|
|
|
2019
2508
|
## Core principles
|
|
2020
2509
|
- ACT, don't just talk. When the user asks you to do something, USE YOUR TOOLS immediately. Never say "I could do X" \u2014 just do X.
|
|
@@ -2030,7 +2519,7 @@ For complex tasks, work through multiple steps:
|
|
|
2030
2519
|
4. **Summarize** the final result clearly.
|
|
2031
2520
|
|
|
2032
2521
|
## Environment
|
|
2033
|
-
- OS: ${
|
|
2522
|
+
- OS: ${os4}
|
|
2034
2523
|
- Home: ${homeDir}
|
|
2035
2524
|
- Documents: ${homeDir}/Documents
|
|
2036
2525
|
- Desktop: ${homeDir}/Desktop
|
|
@@ -2439,7 +2928,7 @@ var init_rule_loader = __esm({
|
|
|
2439
2928
|
});
|
|
2440
2929
|
|
|
2441
2930
|
// ../security/dist/security-manager.js
|
|
2442
|
-
import
|
|
2931
|
+
import crypto4 from "node:crypto";
|
|
2443
2932
|
var SecurityManager;
|
|
2444
2933
|
var init_security_manager = __esm({
|
|
2445
2934
|
"../security/dist/security-manager.js"() {
|
|
@@ -2456,7 +2945,7 @@ var init_security_manager = __esm({
|
|
|
2456
2945
|
evaluate(context) {
|
|
2457
2946
|
const evaluation = this.ruleEngine.evaluate(context);
|
|
2458
2947
|
const auditEntry = {
|
|
2459
|
-
id:
|
|
2948
|
+
id: crypto4.randomUUID(),
|
|
2460
2949
|
timestamp: evaluation.timestamp,
|
|
2461
2950
|
userId: context.userId,
|
|
2462
2951
|
action: context.action,
|
|
@@ -2767,10 +3256,80 @@ var init_activity_tracker = __esm({
|
|
|
2767
3256
|
// ../skills/dist/plugin-loader.js
|
|
2768
3257
|
import fs3 from "node:fs";
|
|
2769
3258
|
import path3 from "node:path";
|
|
3259
|
+
var PluginLoader;
|
|
2770
3260
|
var init_plugin_loader = __esm({
|
|
2771
3261
|
"../skills/dist/plugin-loader.js"() {
|
|
2772
3262
|
"use strict";
|
|
2773
3263
|
init_skill();
|
|
3264
|
+
PluginLoader = class {
|
|
3265
|
+
/**
|
|
3266
|
+
* Load all plugin skills from .js files in the given directory.
|
|
3267
|
+
* Each file must default-export a class that extends Skill.
|
|
3268
|
+
* Files that fail to load are skipped with a warning logged to console.
|
|
3269
|
+
*/
|
|
3270
|
+
async loadFromDirectory(dirPath) {
|
|
3271
|
+
const resolvedDir = path3.resolve(dirPath);
|
|
3272
|
+
let entries;
|
|
3273
|
+
try {
|
|
3274
|
+
entries = await fs3.promises.readdir(resolvedDir);
|
|
3275
|
+
} catch (error) {
|
|
3276
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3277
|
+
console.warn(`PluginLoader: failed to read directory "${resolvedDir}": ${message}`);
|
|
3278
|
+
return [];
|
|
3279
|
+
}
|
|
3280
|
+
const jsFiles = entries.filter((entry) => entry.endsWith(".js"));
|
|
3281
|
+
const skills = [];
|
|
3282
|
+
for (const file of jsFiles) {
|
|
3283
|
+
const filePath = path3.join(resolvedDir, file);
|
|
3284
|
+
try {
|
|
3285
|
+
const skill = await this.loadFromFile(filePath);
|
|
3286
|
+
skills.push(skill);
|
|
3287
|
+
} catch (error) {
|
|
3288
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3289
|
+
console.warn(`PluginLoader: skipping "${filePath}": ${message}`);
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
return skills;
|
|
3293
|
+
}
|
|
3294
|
+
/**
|
|
3295
|
+
* Load a single plugin skill from a .js file.
|
|
3296
|
+
* The module must default-export a class that extends Skill.
|
|
3297
|
+
*/
|
|
3298
|
+
async loadFromFile(filePath) {
|
|
3299
|
+
const resolvedPath = path3.resolve(filePath);
|
|
3300
|
+
const fileUrl = `file:///${resolvedPath.replace(/\\/g, "/")}`;
|
|
3301
|
+
const mod = await import(fileUrl);
|
|
3302
|
+
const ExportedClass = mod.default;
|
|
3303
|
+
if (typeof ExportedClass !== "function") {
|
|
3304
|
+
throw new Error(`Module "${resolvedPath}" does not have a default export that is a class`);
|
|
3305
|
+
}
|
|
3306
|
+
const instance = new ExportedClass();
|
|
3307
|
+
if (!(instance instanceof Skill)) {
|
|
3308
|
+
throw new Error(`Default export of "${resolvedPath}" does not extend Skill`);
|
|
3309
|
+
}
|
|
3310
|
+
this.validateMetadata(instance, resolvedPath);
|
|
3311
|
+
return instance;
|
|
3312
|
+
}
|
|
3313
|
+
validateMetadata(skill, filePath) {
|
|
3314
|
+
const { metadata } = skill;
|
|
3315
|
+
if (!metadata) {
|
|
3316
|
+
throw new Error(`Plugin "${filePath}" is missing metadata`);
|
|
3317
|
+
}
|
|
3318
|
+
if (!metadata.name || typeof metadata.name !== "string") {
|
|
3319
|
+
throw new Error(`Plugin "${filePath}" has invalid or missing metadata.name`);
|
|
3320
|
+
}
|
|
3321
|
+
if (!metadata.description || typeof metadata.description !== "string") {
|
|
3322
|
+
throw new Error(`Plugin "${filePath}" has invalid or missing metadata.description`);
|
|
3323
|
+
}
|
|
3324
|
+
const validRiskLevels = ["read", "write", "destructive", "admin"];
|
|
3325
|
+
if (!validRiskLevels.includes(metadata.riskLevel)) {
|
|
3326
|
+
throw new Error(`Plugin "${filePath}" has invalid metadata.riskLevel: "${String(metadata.riskLevel)}"`);
|
|
3327
|
+
}
|
|
3328
|
+
if (!metadata.version || typeof metadata.version !== "string") {
|
|
3329
|
+
throw new Error(`Plugin "${filePath}" has invalid or missing metadata.version`);
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
};
|
|
2774
3333
|
}
|
|
2775
3334
|
});
|
|
2776
3335
|
|
|
@@ -3955,7 +4514,8 @@ var init_delegate = __esm({
|
|
|
3955
4514
|
}
|
|
3956
4515
|
const requestedIterations = input2.max_iterations;
|
|
3957
4516
|
const maxIterations = requestedIterations ? Math.max(1, Math.min(MAX_ALLOWED_ITERATIONS, Math.round(requestedIterations))) : DEFAULT_MAX_ITERATIONS;
|
|
3958
|
-
const
|
|
4517
|
+
const progressCb = context.onProgress ?? this.onProgress;
|
|
4518
|
+
const tracker = context.tracker ? context.tracker : new ActivityTracker(progressCb);
|
|
3959
4519
|
tracker.ping("starting", { maxIterations });
|
|
3960
4520
|
const tools = this.buildSubAgentTools();
|
|
3961
4521
|
const systemPrompt = "You are a sub-agent of Alfred, a personal AI assistant. Complete the assigned task using the tools available to you. Work step by step: use tools to gather information, then synthesize a clear result. Be concise and return only the final answer when done.";
|
|
@@ -5657,8 +6217,8 @@ var init_microsoft_provider = __esm({
|
|
|
5657
6217
|
const data = await res.json();
|
|
5658
6218
|
this.accessToken = data.access_token;
|
|
5659
6219
|
}
|
|
5660
|
-
async graphRequest(
|
|
5661
|
-
const res = await fetch(`https://graph.microsoft.com/v1.0${
|
|
6220
|
+
async graphRequest(path14, options = {}) {
|
|
6221
|
+
const res = await fetch(`https://graph.microsoft.com/v1.0${path14}`, {
|
|
5662
6222
|
...options,
|
|
5663
6223
|
headers: {
|
|
5664
6224
|
Authorization: `Bearer ${this.accessToken}`,
|
|
@@ -5668,7 +6228,7 @@ var init_microsoft_provider = __esm({
|
|
|
5668
6228
|
});
|
|
5669
6229
|
if (res.status === 401) {
|
|
5670
6230
|
await this.refreshAccessToken();
|
|
5671
|
-
const retry = await fetch(`https://graph.microsoft.com/v1.0${
|
|
6231
|
+
const retry = await fetch(`https://graph.microsoft.com/v1.0${path14}`, {
|
|
5672
6232
|
...options,
|
|
5673
6233
|
headers: {
|
|
5674
6234
|
Authorization: `Bearer ${this.accessToken}`,
|
|
@@ -6002,22 +6562,1132 @@ var init_calendar = __esm({
|
|
|
6002
6562
|
}
|
|
6003
6563
|
});
|
|
6004
6564
|
|
|
6005
|
-
// ../skills/dist/
|
|
6006
|
-
var
|
|
6007
|
-
|
|
6565
|
+
// ../skills/dist/built-in/cross-platform.js
|
|
6566
|
+
var CrossPlatformSkill;
|
|
6567
|
+
var init_cross_platform = __esm({
|
|
6568
|
+
"../skills/dist/built-in/cross-platform.js"() {
|
|
6008
6569
|
"use strict";
|
|
6009
6570
|
init_skill();
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6571
|
+
CrossPlatformSkill = class extends Skill {
|
|
6572
|
+
users;
|
|
6573
|
+
linkTokens;
|
|
6574
|
+
adapters;
|
|
6575
|
+
metadata = {
|
|
6576
|
+
name: "cross_platform",
|
|
6577
|
+
description: "Manage cross-platform identity linking and messaging. Actions: link_start (generate a linking code on current platform), link_confirm (enter a code from another platform to link accounts), send_message (send a message to a linked platform), list_identities (show all linked platforms), unlink (remove a platform link).",
|
|
6578
|
+
riskLevel: "write",
|
|
6579
|
+
version: "1.0.0",
|
|
6580
|
+
inputSchema: {
|
|
6581
|
+
type: "object",
|
|
6582
|
+
properties: {
|
|
6583
|
+
action: {
|
|
6584
|
+
type: "string",
|
|
6585
|
+
enum: ["link_start", "link_confirm", "send_message", "list_identities", "unlink"],
|
|
6586
|
+
description: "The action to perform"
|
|
6587
|
+
},
|
|
6588
|
+
code: {
|
|
6589
|
+
type: "string",
|
|
6590
|
+
description: "The 6-digit linking code (for link_confirm)"
|
|
6591
|
+
},
|
|
6592
|
+
platform: {
|
|
6593
|
+
type: "string",
|
|
6594
|
+
description: "Target platform (for send_message or unlink)"
|
|
6595
|
+
},
|
|
6596
|
+
chat_id: {
|
|
6597
|
+
type: "string",
|
|
6598
|
+
description: "Target chat ID (for send_message)"
|
|
6599
|
+
},
|
|
6600
|
+
message: {
|
|
6601
|
+
type: "string",
|
|
6602
|
+
description: "Message text to send (for send_message)"
|
|
6603
|
+
}
|
|
6604
|
+
},
|
|
6605
|
+
required: ["action"]
|
|
6606
|
+
}
|
|
6607
|
+
};
|
|
6608
|
+
constructor(users, linkTokens, adapters) {
|
|
6609
|
+
super();
|
|
6610
|
+
this.users = users;
|
|
6611
|
+
this.linkTokens = linkTokens;
|
|
6612
|
+
this.adapters = adapters;
|
|
6613
|
+
}
|
|
6614
|
+
async execute(input2, context) {
|
|
6615
|
+
const action = input2.action;
|
|
6616
|
+
switch (action) {
|
|
6617
|
+
case "link_start":
|
|
6618
|
+
return this.linkStart(context);
|
|
6619
|
+
case "link_confirm":
|
|
6620
|
+
return this.linkConfirm(input2, context);
|
|
6621
|
+
case "send_message":
|
|
6622
|
+
return this.sendMessage(input2);
|
|
6623
|
+
case "list_identities":
|
|
6624
|
+
return this.listIdentities(context);
|
|
6625
|
+
case "unlink":
|
|
6626
|
+
return this.unlink(input2, context);
|
|
6627
|
+
default:
|
|
6628
|
+
return { success: false, error: `Unknown action: ${action}` };
|
|
6629
|
+
}
|
|
6630
|
+
}
|
|
6631
|
+
async linkStart(context) {
|
|
6632
|
+
this.linkTokens.cleanup();
|
|
6633
|
+
const token = this.linkTokens.create(context.userId, context.platform);
|
|
6634
|
+
return {
|
|
6635
|
+
success: true,
|
|
6636
|
+
data: { code: token.code, expiresAt: token.expiresAt },
|
|
6637
|
+
display: `Your linking code is: **${token.code}**
|
|
6638
|
+
|
|
6639
|
+
Enter this code on your other platform within 10 minutes using:
|
|
6640
|
+
"Link my account with code ${token.code}"`
|
|
6641
|
+
};
|
|
6642
|
+
}
|
|
6643
|
+
async linkConfirm(input2, context) {
|
|
6644
|
+
const code = input2.code;
|
|
6645
|
+
if (!code) {
|
|
6646
|
+
return { success: false, error: 'Missing required field "code"' };
|
|
6647
|
+
}
|
|
6648
|
+
const token = this.linkTokens.findByCode(code.trim());
|
|
6649
|
+
if (!token) {
|
|
6650
|
+
return {
|
|
6651
|
+
success: false,
|
|
6652
|
+
error: "Invalid or expired linking code. Please generate a new one."
|
|
6653
|
+
};
|
|
6654
|
+
}
|
|
6655
|
+
if (token.userId === context.userId) {
|
|
6656
|
+
return {
|
|
6657
|
+
success: false,
|
|
6658
|
+
error: "Cannot link an account to itself. Use the code on a different platform."
|
|
6659
|
+
};
|
|
6660
|
+
}
|
|
6661
|
+
const existingMaster1 = this.users.getMasterUserId(token.userId);
|
|
6662
|
+
const existingMaster2 = this.users.getMasterUserId(context.userId);
|
|
6663
|
+
let masterUserId;
|
|
6664
|
+
if (existingMaster1 !== token.userId) {
|
|
6665
|
+
masterUserId = existingMaster1;
|
|
6666
|
+
} else if (existingMaster2 !== context.userId) {
|
|
6667
|
+
masterUserId = existingMaster2;
|
|
6668
|
+
} else {
|
|
6669
|
+
masterUserId = token.userId;
|
|
6670
|
+
}
|
|
6671
|
+
if (token.userId !== masterUserId) {
|
|
6672
|
+
this.users.setMasterUser(token.userId, masterUserId);
|
|
6673
|
+
}
|
|
6674
|
+
if (context.userId !== masterUserId) {
|
|
6675
|
+
this.users.setMasterUser(context.userId, masterUserId);
|
|
6676
|
+
}
|
|
6677
|
+
this.linkTokens.consume(token.id);
|
|
6678
|
+
const tokenUser = this.users.findById(token.userId);
|
|
6679
|
+
const platformName = token.platform;
|
|
6680
|
+
return {
|
|
6681
|
+
success: true,
|
|
6682
|
+
data: { masterUserId, linkedPlatform: platformName },
|
|
6683
|
+
display: `Account linked successfully! Your ${platformName} account (${tokenUser?.displayName ?? tokenUser?.username ?? "unknown"}) is now linked to this ${context.platform} account.
|
|
6684
|
+
|
|
6685
|
+
Your memories, preferences, and context are now shared across platforms.`
|
|
6686
|
+
};
|
|
6687
|
+
}
|
|
6688
|
+
async sendMessage(input2) {
|
|
6689
|
+
const platform = input2.platform;
|
|
6690
|
+
const chatId = input2.chat_id;
|
|
6691
|
+
const message = input2.message;
|
|
6692
|
+
if (!platform) {
|
|
6693
|
+
return { success: false, error: 'Missing required field "platform"' };
|
|
6694
|
+
}
|
|
6695
|
+
if (!chatId) {
|
|
6696
|
+
return { success: false, error: 'Missing required field "chat_id"' };
|
|
6697
|
+
}
|
|
6698
|
+
if (!message) {
|
|
6699
|
+
return { success: false, error: 'Missing required field "message"' };
|
|
6700
|
+
}
|
|
6701
|
+
const adapter = this.adapters.get(platform);
|
|
6702
|
+
if (!adapter) {
|
|
6703
|
+
return {
|
|
6704
|
+
success: false,
|
|
6705
|
+
error: `Platform "${platform}" is not connected. Available: ${[...this.adapters.keys()].join(", ")}`
|
|
6706
|
+
};
|
|
6707
|
+
}
|
|
6708
|
+
try {
|
|
6709
|
+
const messageId = await adapter.sendMessage(chatId, message);
|
|
6710
|
+
return {
|
|
6711
|
+
success: true,
|
|
6712
|
+
data: { messageId, platform, chatId },
|
|
6713
|
+
display: `Message sent to ${platform} (chat ${chatId}).`
|
|
6714
|
+
};
|
|
6715
|
+
} catch (err) {
|
|
6716
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6717
|
+
return { success: false, error: `Failed to send message: ${msg}` };
|
|
6718
|
+
}
|
|
6719
|
+
}
|
|
6720
|
+
async listIdentities(context) {
|
|
6721
|
+
const masterUserId = this.users.getMasterUserId(context.userId);
|
|
6722
|
+
const linkedUsers = this.users.getLinkedUsers(masterUserId);
|
|
6723
|
+
if (linkedUsers.length <= 1) {
|
|
6724
|
+
return {
|
|
6725
|
+
success: true,
|
|
6726
|
+
data: { identities: linkedUsers },
|
|
6727
|
+
display: 'No linked accounts found. To link another platform, use:\n"Start linking my account" on the platform you want to link from, then enter the code on the other platform.'
|
|
6728
|
+
};
|
|
6729
|
+
}
|
|
6730
|
+
const lines = linkedUsers.map((u) => {
|
|
6731
|
+
const isCurrent = u.id === context.userId ? " (current)" : "";
|
|
6732
|
+
const name = u.displayName ?? u.username ?? u.platformUserId;
|
|
6733
|
+
return `- **${u.platform}**: ${name}${isCurrent}`;
|
|
6734
|
+
});
|
|
6735
|
+
return {
|
|
6736
|
+
success: true,
|
|
6737
|
+
data: { identities: linkedUsers.map((u) => ({ platform: u.platform, username: u.username, displayName: u.displayName })) },
|
|
6738
|
+
display: `Linked accounts:
|
|
6739
|
+
${lines.join("\n")}`
|
|
6740
|
+
};
|
|
6741
|
+
}
|
|
6742
|
+
async unlink(input2, context) {
|
|
6743
|
+
const platform = input2.platform;
|
|
6744
|
+
if (!platform) {
|
|
6745
|
+
return { success: false, error: 'Missing required field "platform"' };
|
|
6746
|
+
}
|
|
6747
|
+
const masterUserId = this.users.getMasterUserId(context.userId);
|
|
6748
|
+
const linkedUsers = this.users.getLinkedUsers(masterUserId);
|
|
6749
|
+
const targetUser = linkedUsers.find((u) => u.platform === platform && u.id !== context.userId);
|
|
6750
|
+
if (!targetUser) {
|
|
6751
|
+
return {
|
|
6752
|
+
success: false,
|
|
6753
|
+
error: `No linked account found on platform "${platform}".`
|
|
6754
|
+
};
|
|
6755
|
+
}
|
|
6756
|
+
this.users.setMasterUser(targetUser.id, targetUser.id);
|
|
6757
|
+
return {
|
|
6758
|
+
success: true,
|
|
6759
|
+
data: { unlinkedPlatform: platform, unlinkedUserId: targetUser.id },
|
|
6760
|
+
display: `Unlinked ${platform} account (${targetUser.displayName ?? targetUser.username ?? targetUser.platformUserId}).`
|
|
6761
|
+
};
|
|
6762
|
+
}
|
|
6763
|
+
};
|
|
6764
|
+
}
|
|
6765
|
+
});
|
|
6766
|
+
|
|
6767
|
+
// ../skills/dist/built-in/background-task.js
|
|
6768
|
+
var BackgroundTaskSkill;
|
|
6769
|
+
var init_background_task = __esm({
|
|
6770
|
+
"../skills/dist/built-in/background-task.js"() {
|
|
6771
|
+
"use strict";
|
|
6772
|
+
init_skill();
|
|
6773
|
+
BackgroundTaskSkill = class extends Skill {
|
|
6774
|
+
taskRepo;
|
|
6775
|
+
metadata = {
|
|
6776
|
+
name: "background_task",
|
|
6777
|
+
description: 'Schedule, list, or cancel background tasks that run independently. Use "schedule" to queue a skill to execute in the background (user will be notified when done). Use "list" to see active/recent tasks. Use "cancel" to stop a pending or running task.',
|
|
6778
|
+
riskLevel: "write",
|
|
6779
|
+
version: "1.0.0",
|
|
6780
|
+
inputSchema: {
|
|
6781
|
+
type: "object",
|
|
6782
|
+
properties: {
|
|
6783
|
+
action: {
|
|
6784
|
+
type: "string",
|
|
6785
|
+
enum: ["schedule", "list", "cancel"],
|
|
6786
|
+
description: "The background task action to perform"
|
|
6787
|
+
},
|
|
6788
|
+
description: {
|
|
6789
|
+
type: "string",
|
|
6790
|
+
description: "Human-readable description of what the task does (for schedule)"
|
|
6791
|
+
},
|
|
6792
|
+
skill_name: {
|
|
6793
|
+
type: "string",
|
|
6794
|
+
description: "The skill to run in the background (for schedule)"
|
|
6795
|
+
},
|
|
6796
|
+
skill_input: {
|
|
6797
|
+
type: "object",
|
|
6798
|
+
description: "Input to pass to the skill (for schedule)"
|
|
6799
|
+
},
|
|
6800
|
+
task_id: {
|
|
6801
|
+
type: "string",
|
|
6802
|
+
description: "Task ID (for cancel)"
|
|
6803
|
+
}
|
|
6804
|
+
},
|
|
6805
|
+
required: ["action"]
|
|
6806
|
+
}
|
|
6807
|
+
};
|
|
6808
|
+
constructor(taskRepo) {
|
|
6809
|
+
super();
|
|
6810
|
+
this.taskRepo = taskRepo;
|
|
6811
|
+
}
|
|
6812
|
+
async execute(input2, context) {
|
|
6813
|
+
const action = input2.action;
|
|
6814
|
+
switch (action) {
|
|
6815
|
+
case "schedule":
|
|
6816
|
+
return this.scheduleTask(input2, context);
|
|
6817
|
+
case "list":
|
|
6818
|
+
return this.listTasks(context);
|
|
6819
|
+
case "cancel":
|
|
6820
|
+
return this.cancelTask(input2);
|
|
6821
|
+
default:
|
|
6822
|
+
return {
|
|
6823
|
+
success: false,
|
|
6824
|
+
error: `Unknown action: "${String(action)}". Valid actions: schedule, list, cancel`
|
|
6825
|
+
};
|
|
6826
|
+
}
|
|
6827
|
+
}
|
|
6828
|
+
scheduleTask(input2, context) {
|
|
6829
|
+
const description = input2.description;
|
|
6830
|
+
const skillName = input2.skill_name;
|
|
6831
|
+
const skillInput = input2.skill_input;
|
|
6832
|
+
if (!description || typeof description !== "string") {
|
|
6833
|
+
return { success: false, error: 'Missing required field "description" for schedule action' };
|
|
6834
|
+
}
|
|
6835
|
+
if (!skillName || typeof skillName !== "string") {
|
|
6836
|
+
return { success: false, error: 'Missing required field "skill_name" for schedule action' };
|
|
6837
|
+
}
|
|
6838
|
+
const task = this.taskRepo.create(context.userId, context.platform, context.chatId, description, skillName, JSON.stringify(skillInput ?? {}));
|
|
6839
|
+
return {
|
|
6840
|
+
success: true,
|
|
6841
|
+
data: { taskId: task.id, description, skillName, status: task.status },
|
|
6842
|
+
display: `Background task scheduled (${task.id}): "${description}" using skill "${skillName}". You'll be notified when it completes.`
|
|
6843
|
+
};
|
|
6844
|
+
}
|
|
6845
|
+
listTasks(context) {
|
|
6846
|
+
const tasks = this.taskRepo.getByUser(context.userId);
|
|
6847
|
+
if (tasks.length === 0) {
|
|
6848
|
+
return {
|
|
6849
|
+
success: true,
|
|
6850
|
+
data: [],
|
|
6851
|
+
display: "No active or recent background tasks."
|
|
6852
|
+
};
|
|
6853
|
+
}
|
|
6854
|
+
const statusIcon = {
|
|
6855
|
+
pending: "\u23F3",
|
|
6856
|
+
running: "\u25B6\uFE0F",
|
|
6857
|
+
completed: "\u2705",
|
|
6858
|
+
failed: "\u274C"
|
|
6859
|
+
};
|
|
6860
|
+
const lines = tasks.map((t) => `- ${statusIcon[t.status] ?? "?"} ${t.id}: "${t.description}" [${t.status}] (${t.skillName})`);
|
|
6861
|
+
return {
|
|
6862
|
+
success: true,
|
|
6863
|
+
data: tasks.map((t) => ({
|
|
6864
|
+
taskId: t.id,
|
|
6865
|
+
description: t.description,
|
|
6866
|
+
status: t.status,
|
|
6867
|
+
skillName: t.skillName,
|
|
6868
|
+
createdAt: t.createdAt,
|
|
6869
|
+
completedAt: t.completedAt
|
|
6870
|
+
})),
|
|
6871
|
+
display: `Background tasks:
|
|
6872
|
+
${lines.join("\n")}`
|
|
6873
|
+
};
|
|
6874
|
+
}
|
|
6875
|
+
cancelTask(input2) {
|
|
6876
|
+
const taskId = input2.task_id;
|
|
6877
|
+
if (!taskId || typeof taskId !== "string") {
|
|
6878
|
+
return { success: false, error: 'Missing required field "task_id" for cancel action' };
|
|
6879
|
+
}
|
|
6880
|
+
const cancelled = this.taskRepo.cancel(taskId);
|
|
6881
|
+
if (!cancelled) {
|
|
6882
|
+
return {
|
|
6883
|
+
success: false,
|
|
6884
|
+
error: `Task "${taskId}" not found or already completed`
|
|
6885
|
+
};
|
|
6886
|
+
}
|
|
6887
|
+
return {
|
|
6888
|
+
success: true,
|
|
6889
|
+
data: { taskId },
|
|
6890
|
+
display: `Background task "${taskId}" cancelled.`
|
|
6891
|
+
};
|
|
6892
|
+
}
|
|
6893
|
+
};
|
|
6894
|
+
}
|
|
6895
|
+
});
|
|
6896
|
+
|
|
6897
|
+
// ../skills/dist/built-in/scheduled-task.js
|
|
6898
|
+
var ScheduledTaskSkill;
|
|
6899
|
+
var init_scheduled_task = __esm({
|
|
6900
|
+
"../skills/dist/built-in/scheduled-task.js"() {
|
|
6901
|
+
"use strict";
|
|
6902
|
+
init_skill();
|
|
6903
|
+
ScheduledTaskSkill = class extends Skill {
|
|
6904
|
+
actionRepo;
|
|
6905
|
+
metadata = {
|
|
6906
|
+
name: "scheduled_task",
|
|
6907
|
+
description: 'Create, list, enable, disable, or delete scheduled actions that run automatically on a recurring basis. Supports cron expressions (e.g. "0 9 * * *" for daily at 9 AM), intervals (in minutes), and one-time schedules. Each scheduled action executes a skill or sends a prompt to the LLM at the configured time.',
|
|
6908
|
+
riskLevel: "write",
|
|
6909
|
+
version: "1.0.0",
|
|
6910
|
+
inputSchema: {
|
|
6911
|
+
type: "object",
|
|
6912
|
+
properties: {
|
|
6913
|
+
action: {
|
|
6914
|
+
type: "string",
|
|
6915
|
+
enum: ["create", "list", "enable", "disable", "delete"],
|
|
6916
|
+
description: "The scheduled task action to perform"
|
|
6917
|
+
},
|
|
6918
|
+
name: {
|
|
6919
|
+
type: "string",
|
|
6920
|
+
description: "Name for the scheduled action (for create)"
|
|
6921
|
+
},
|
|
6922
|
+
description: {
|
|
6923
|
+
type: "string",
|
|
6924
|
+
description: "What the scheduled action does (for create)"
|
|
6925
|
+
},
|
|
6926
|
+
schedule_type: {
|
|
6927
|
+
type: "string",
|
|
6928
|
+
enum: ["cron", "interval", "once"],
|
|
6929
|
+
description: "Type of schedule: cron expression, interval in minutes, or one-time ISO date (for create)"
|
|
6930
|
+
},
|
|
6931
|
+
schedule_value: {
|
|
6932
|
+
type: "string",
|
|
6933
|
+
description: "Schedule value: cron expression, minutes as string, or ISO date (for create)"
|
|
6934
|
+
},
|
|
6935
|
+
skill_name: {
|
|
6936
|
+
type: "string",
|
|
6937
|
+
description: "The skill to execute on schedule (for create)"
|
|
6938
|
+
},
|
|
6939
|
+
skill_input: {
|
|
6940
|
+
type: "object",
|
|
6941
|
+
description: "Input to pass to the skill (for create)"
|
|
6942
|
+
},
|
|
6943
|
+
prompt_template: {
|
|
6944
|
+
type: "string",
|
|
6945
|
+
description: "Optional LLM prompt to run instead of a skill (for create)"
|
|
6946
|
+
},
|
|
6947
|
+
action_id: {
|
|
6948
|
+
type: "string",
|
|
6949
|
+
description: "Scheduled action ID (for enable, disable, delete)"
|
|
6950
|
+
}
|
|
6951
|
+
},
|
|
6952
|
+
required: ["action"]
|
|
6953
|
+
}
|
|
6954
|
+
};
|
|
6955
|
+
constructor(actionRepo) {
|
|
6956
|
+
super();
|
|
6957
|
+
this.actionRepo = actionRepo;
|
|
6958
|
+
}
|
|
6959
|
+
async execute(input2, context) {
|
|
6960
|
+
const action = input2.action;
|
|
6961
|
+
switch (action) {
|
|
6962
|
+
case "create":
|
|
6963
|
+
return this.createAction(input2, context);
|
|
6964
|
+
case "list":
|
|
6965
|
+
return this.listActions(context);
|
|
6966
|
+
case "enable":
|
|
6967
|
+
return this.toggleAction(input2, true);
|
|
6968
|
+
case "disable":
|
|
6969
|
+
return this.toggleAction(input2, false);
|
|
6970
|
+
case "delete":
|
|
6971
|
+
return this.deleteAction(input2);
|
|
6972
|
+
default:
|
|
6973
|
+
return {
|
|
6974
|
+
success: false,
|
|
6975
|
+
error: `Unknown action: "${String(action)}". Valid actions: create, list, enable, disable, delete`
|
|
6976
|
+
};
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6979
|
+
createAction(input2, context) {
|
|
6980
|
+
const name = input2.name;
|
|
6981
|
+
const description = input2.description;
|
|
6982
|
+
const scheduleType = input2.schedule_type;
|
|
6983
|
+
const scheduleValue = input2.schedule_value;
|
|
6984
|
+
const skillName = input2.skill_name;
|
|
6985
|
+
const skillInput = input2.skill_input;
|
|
6986
|
+
const promptTemplate = input2.prompt_template;
|
|
6987
|
+
if (!name || typeof name !== "string") {
|
|
6988
|
+
return { success: false, error: 'Missing required field "name" for create action' };
|
|
6989
|
+
}
|
|
6990
|
+
if (!description || typeof description !== "string") {
|
|
6991
|
+
return { success: false, error: 'Missing required field "description" for create action' };
|
|
6992
|
+
}
|
|
6993
|
+
if (!scheduleType || !["cron", "interval", "once"].includes(scheduleType)) {
|
|
6994
|
+
return { success: false, error: 'Missing or invalid "schedule_type". Must be "cron", "interval", or "once"' };
|
|
6995
|
+
}
|
|
6996
|
+
if (!scheduleValue || typeof scheduleValue !== "string") {
|
|
6997
|
+
return { success: false, error: 'Missing required field "schedule_value" for create action' };
|
|
6998
|
+
}
|
|
6999
|
+
if (!skillName || typeof skillName !== "string") {
|
|
7000
|
+
return { success: false, error: 'Missing required field "skill_name" for create action' };
|
|
7001
|
+
}
|
|
7002
|
+
if (scheduleType === "interval") {
|
|
7003
|
+
const minutes = parseInt(scheduleValue, 10);
|
|
7004
|
+
if (isNaN(minutes) || minutes <= 0) {
|
|
7005
|
+
return { success: false, error: "For interval schedule, value must be a positive number of minutes" };
|
|
7006
|
+
}
|
|
7007
|
+
}
|
|
7008
|
+
if (scheduleType === "cron") {
|
|
7009
|
+
const parts = scheduleValue.trim().split(/\s+/);
|
|
7010
|
+
if (parts.length !== 5) {
|
|
7011
|
+
return { success: false, error: "Cron expression must have 5 fields: minute hour dayOfMonth month dayOfWeek" };
|
|
7012
|
+
}
|
|
7013
|
+
}
|
|
7014
|
+
if (scheduleType === "once") {
|
|
7015
|
+
const date = new Date(scheduleValue);
|
|
7016
|
+
if (isNaN(date.getTime())) {
|
|
7017
|
+
return { success: false, error: "For once schedule, value must be a valid ISO date string" };
|
|
7018
|
+
}
|
|
7019
|
+
if (date.getTime() <= Date.now()) {
|
|
7020
|
+
return { success: false, error: "The scheduled time is in the past. Please specify a future time." };
|
|
7021
|
+
}
|
|
7022
|
+
}
|
|
7023
|
+
const entry = this.actionRepo.create({
|
|
7024
|
+
userId: context.userId,
|
|
7025
|
+
platform: context.platform,
|
|
7026
|
+
chatId: context.chatId,
|
|
7027
|
+
name,
|
|
7028
|
+
description,
|
|
7029
|
+
scheduleType,
|
|
7030
|
+
scheduleValue,
|
|
7031
|
+
skillName,
|
|
7032
|
+
skillInput: JSON.stringify(skillInput ?? {}),
|
|
7033
|
+
promptTemplate
|
|
7034
|
+
});
|
|
7035
|
+
const scheduleLabel = scheduleType === "cron" ? `cron: ${scheduleValue}` : scheduleType === "interval" ? `every ${scheduleValue} minutes` : `once at ${scheduleValue}`;
|
|
7036
|
+
return {
|
|
7037
|
+
success: true,
|
|
7038
|
+
data: { actionId: entry.id, name, scheduleType, scheduleValue, skillName },
|
|
7039
|
+
display: `Scheduled action created (${entry.id}): "${name}" \u2014 ${scheduleLabel}, running "${skillName}"${entry.nextRunAt ? `. Next run: ${entry.nextRunAt}` : ""}`
|
|
7040
|
+
};
|
|
7041
|
+
}
|
|
7042
|
+
listActions(context) {
|
|
7043
|
+
const actions = this.actionRepo.getByUser(context.userId);
|
|
7044
|
+
if (actions.length === 0) {
|
|
7045
|
+
return {
|
|
7046
|
+
success: true,
|
|
7047
|
+
data: [],
|
|
7048
|
+
display: "No scheduled actions."
|
|
7049
|
+
};
|
|
7050
|
+
}
|
|
7051
|
+
const lines = actions.map((a) => {
|
|
7052
|
+
const status = a.enabled ? "\u2705" : "\u23F8\uFE0F";
|
|
7053
|
+
const scheduleLabel = a.scheduleType === "cron" ? `cron: ${a.scheduleValue}` : a.scheduleType === "interval" ? `every ${a.scheduleValue} min` : `once: ${a.scheduleValue}`;
|
|
7054
|
+
const nextRun = a.nextRunAt ? ` | next: ${a.nextRunAt}` : "";
|
|
7055
|
+
return `- ${status} ${a.id}: "${a.name}" [${scheduleLabel}] \u2192 ${a.skillName}${nextRun}`;
|
|
7056
|
+
});
|
|
7057
|
+
return {
|
|
7058
|
+
success: true,
|
|
7059
|
+
data: actions.map((a) => ({
|
|
7060
|
+
actionId: a.id,
|
|
7061
|
+
name: a.name,
|
|
7062
|
+
scheduleType: a.scheduleType,
|
|
7063
|
+
scheduleValue: a.scheduleValue,
|
|
7064
|
+
skillName: a.skillName,
|
|
7065
|
+
enabled: a.enabled,
|
|
7066
|
+
nextRunAt: a.nextRunAt,
|
|
7067
|
+
lastRunAt: a.lastRunAt
|
|
7068
|
+
})),
|
|
7069
|
+
display: `Scheduled actions:
|
|
7070
|
+
${lines.join("\n")}`
|
|
7071
|
+
};
|
|
7072
|
+
}
|
|
7073
|
+
toggleAction(input2, enabled) {
|
|
7074
|
+
const actionId = input2.action_id;
|
|
7075
|
+
if (!actionId || typeof actionId !== "string") {
|
|
7076
|
+
return { success: false, error: `Missing required field "action_id" for ${enabled ? "enable" : "disable"} action` };
|
|
7077
|
+
}
|
|
7078
|
+
const updated = this.actionRepo.setEnabled(actionId, enabled);
|
|
7079
|
+
if (!updated) {
|
|
7080
|
+
return { success: false, error: `Scheduled action "${actionId}" not found` };
|
|
7081
|
+
}
|
|
7082
|
+
return {
|
|
7083
|
+
success: true,
|
|
7084
|
+
data: { actionId, enabled },
|
|
7085
|
+
display: `Scheduled action "${actionId}" ${enabled ? "enabled" : "disabled"}.`
|
|
7086
|
+
};
|
|
7087
|
+
}
|
|
7088
|
+
deleteAction(input2) {
|
|
7089
|
+
const actionId = input2.action_id;
|
|
7090
|
+
if (!actionId || typeof actionId !== "string") {
|
|
7091
|
+
return { success: false, error: 'Missing required field "action_id" for delete action' };
|
|
7092
|
+
}
|
|
7093
|
+
const deleted = this.actionRepo.delete(actionId);
|
|
7094
|
+
if (!deleted) {
|
|
7095
|
+
return { success: false, error: `Scheduled action "${actionId}" not found` };
|
|
7096
|
+
}
|
|
7097
|
+
return {
|
|
7098
|
+
success: true,
|
|
7099
|
+
data: { actionId },
|
|
7100
|
+
display: `Scheduled action "${actionId}" deleted.`
|
|
7101
|
+
};
|
|
7102
|
+
}
|
|
7103
|
+
};
|
|
7104
|
+
}
|
|
7105
|
+
});
|
|
7106
|
+
|
|
7107
|
+
// ../skills/dist/mcp/mcp-client.js
|
|
7108
|
+
var MCPClient;
|
|
7109
|
+
var init_mcp_client = __esm({
|
|
7110
|
+
"../skills/dist/mcp/mcp-client.js"() {
|
|
7111
|
+
"use strict";
|
|
7112
|
+
MCPClient = class {
|
|
7113
|
+
serverName;
|
|
7114
|
+
config;
|
|
7115
|
+
logger;
|
|
7116
|
+
client;
|
|
7117
|
+
transport;
|
|
7118
|
+
connected = false;
|
|
7119
|
+
constructor(serverName, config, logger) {
|
|
7120
|
+
this.serverName = serverName;
|
|
7121
|
+
this.config = config;
|
|
7122
|
+
this.logger = logger;
|
|
7123
|
+
}
|
|
7124
|
+
async connect() {
|
|
7125
|
+
try {
|
|
7126
|
+
const { Client: Client2 } = await import("@modelcontextprotocol/sdk/client/index.js");
|
|
7127
|
+
this.client = new Client2({ name: `alfred-${this.serverName}`, version: "1.0.0" }, { capabilities: {} });
|
|
7128
|
+
if (this.config.command) {
|
|
7129
|
+
const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
|
|
7130
|
+
const env = { ...process.env };
|
|
7131
|
+
if (this.config.env) {
|
|
7132
|
+
for (const [key, value] of Object.entries(this.config.env)) {
|
|
7133
|
+
env[key] = value.replace(/\$\{(\w+)\}/g, (_, name) => process.env[name] ?? "");
|
|
7134
|
+
}
|
|
7135
|
+
}
|
|
7136
|
+
this.transport = new StdioClientTransport({
|
|
7137
|
+
command: this.config.command,
|
|
7138
|
+
args: this.config.args ?? [],
|
|
7139
|
+
env
|
|
7140
|
+
});
|
|
7141
|
+
} else if (this.config.url) {
|
|
7142
|
+
const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
|
|
7143
|
+
this.transport = new SSEClientTransport(new URL(this.config.url));
|
|
7144
|
+
} else {
|
|
7145
|
+
throw new Error(`MCP server "${this.serverName}": must specify either command or url`);
|
|
7146
|
+
}
|
|
7147
|
+
await this.client.connect(this.transport);
|
|
7148
|
+
this.connected = true;
|
|
7149
|
+
this.logger.info({ server: this.serverName }, "MCP server connected");
|
|
7150
|
+
} catch (err) {
|
|
7151
|
+
this.logger.error({ server: this.serverName, err }, "Failed to connect MCP server");
|
|
7152
|
+
throw err;
|
|
7153
|
+
}
|
|
7154
|
+
}
|
|
7155
|
+
async listTools() {
|
|
7156
|
+
if (!this.connected || !this.client)
|
|
7157
|
+
return [];
|
|
7158
|
+
try {
|
|
7159
|
+
const result = await this.client.listTools();
|
|
7160
|
+
return (result.tools ?? []).map((t) => ({
|
|
7161
|
+
name: t.name,
|
|
7162
|
+
description: t.description,
|
|
7163
|
+
inputSchema: t.inputSchema ?? { type: "object", properties: {} }
|
|
7164
|
+
}));
|
|
7165
|
+
} catch (err) {
|
|
7166
|
+
this.logger.error({ server: this.serverName, err }, "Failed to list MCP tools");
|
|
7167
|
+
return [];
|
|
7168
|
+
}
|
|
7169
|
+
}
|
|
7170
|
+
async callTool(name, input2) {
|
|
7171
|
+
if (!this.connected || !this.client) {
|
|
7172
|
+
return { content: "MCP server not connected", isError: true };
|
|
7173
|
+
}
|
|
7174
|
+
try {
|
|
7175
|
+
const result = await this.client.callTool({ name, arguments: input2 });
|
|
7176
|
+
const content = (result.content ?? []).map((c) => c.text ?? JSON.stringify(c)).join("\n");
|
|
7177
|
+
return { content, isError: result.isError };
|
|
7178
|
+
} catch (err) {
|
|
7179
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7180
|
+
return { content: `MCP tool error: ${msg}`, isError: true };
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
async disconnect() {
|
|
7184
|
+
if (this.transport) {
|
|
7185
|
+
try {
|
|
7186
|
+
await this.transport.close?.();
|
|
7187
|
+
} catch {
|
|
7188
|
+
}
|
|
7189
|
+
}
|
|
7190
|
+
this.connected = false;
|
|
7191
|
+
this.logger.info({ server: this.serverName }, "MCP server disconnected");
|
|
7192
|
+
}
|
|
7193
|
+
};
|
|
7194
|
+
}
|
|
7195
|
+
});
|
|
7196
|
+
|
|
7197
|
+
// ../skills/dist/mcp/mcp-skill-adapter.js
|
|
7198
|
+
var MCPSkillAdapter;
|
|
7199
|
+
var init_mcp_skill_adapter = __esm({
|
|
7200
|
+
"../skills/dist/mcp/mcp-skill-adapter.js"() {
|
|
7201
|
+
"use strict";
|
|
7202
|
+
init_skill();
|
|
7203
|
+
MCPSkillAdapter = class extends Skill {
|
|
7204
|
+
client;
|
|
7205
|
+
serverName;
|
|
7206
|
+
toolName;
|
|
7207
|
+
metadata;
|
|
7208
|
+
constructor(client, serverName, toolName, description, inputSchema) {
|
|
7209
|
+
super();
|
|
7210
|
+
this.client = client;
|
|
7211
|
+
this.serverName = serverName;
|
|
7212
|
+
this.toolName = toolName;
|
|
7213
|
+
this.metadata = {
|
|
7214
|
+
name: `mcp__${serverName}__${toolName}`,
|
|
7215
|
+
description: `[MCP/${serverName}] ${description || toolName}`,
|
|
7216
|
+
riskLevel: "write",
|
|
7217
|
+
version: "1.0.0",
|
|
7218
|
+
inputSchema
|
|
7219
|
+
};
|
|
7220
|
+
}
|
|
7221
|
+
async execute(input2, _context) {
|
|
7222
|
+
const result = await this.client.callTool(this.toolName, input2);
|
|
7223
|
+
return {
|
|
7224
|
+
success: !result.isError,
|
|
7225
|
+
data: result.content,
|
|
7226
|
+
display: result.content,
|
|
7227
|
+
error: result.isError ? result.content : void 0
|
|
7228
|
+
};
|
|
7229
|
+
}
|
|
7230
|
+
};
|
|
7231
|
+
}
|
|
7232
|
+
});
|
|
7233
|
+
|
|
7234
|
+
// ../skills/dist/mcp/mcp-manager.js
|
|
7235
|
+
var MCPManager;
|
|
7236
|
+
var init_mcp_manager = __esm({
|
|
7237
|
+
"../skills/dist/mcp/mcp-manager.js"() {
|
|
7238
|
+
"use strict";
|
|
7239
|
+
init_mcp_client();
|
|
7240
|
+
init_mcp_skill_adapter();
|
|
7241
|
+
MCPManager = class {
|
|
7242
|
+
logger;
|
|
7243
|
+
clients = [];
|
|
7244
|
+
skills = [];
|
|
7245
|
+
constructor(logger) {
|
|
7246
|
+
this.logger = logger;
|
|
7247
|
+
}
|
|
7248
|
+
async initialize(config) {
|
|
7249
|
+
for (const serverConfig of config.servers) {
|
|
7250
|
+
try {
|
|
7251
|
+
const client = new MCPClient(serverConfig.name, serverConfig, this.logger.child({ mcp: serverConfig.name }));
|
|
7252
|
+
await client.connect();
|
|
7253
|
+
this.clients.push(client);
|
|
7254
|
+
const tools = await client.listTools();
|
|
7255
|
+
for (const tool of tools) {
|
|
7256
|
+
const adapted = new MCPSkillAdapter(client, serverConfig.name, tool.name, tool.description ?? "", tool.inputSchema);
|
|
7257
|
+
this.skills.push(adapted);
|
|
7258
|
+
}
|
|
7259
|
+
this.logger.info({ server: serverConfig.name, tools: tools.length }, "MCP server initialized");
|
|
7260
|
+
} catch (err) {
|
|
7261
|
+
this.logger.error({ server: serverConfig.name, err }, "Failed to initialize MCP server");
|
|
7262
|
+
}
|
|
7263
|
+
}
|
|
7264
|
+
}
|
|
7265
|
+
getSkills() {
|
|
7266
|
+
return this.skills;
|
|
7267
|
+
}
|
|
7268
|
+
async shutdown() {
|
|
7269
|
+
for (const client of this.clients) {
|
|
7270
|
+
await client.disconnect();
|
|
7271
|
+
}
|
|
7272
|
+
this.clients.length = 0;
|
|
7273
|
+
this.skills.length = 0;
|
|
7274
|
+
}
|
|
7275
|
+
};
|
|
7276
|
+
}
|
|
7277
|
+
});
|
|
7278
|
+
|
|
7279
|
+
// ../skills/dist/mcp/index.js
|
|
7280
|
+
var init_mcp = __esm({
|
|
7281
|
+
"../skills/dist/mcp/index.js"() {
|
|
7282
|
+
"use strict";
|
|
7283
|
+
init_mcp_client();
|
|
7284
|
+
init_mcp_skill_adapter();
|
|
7285
|
+
init_mcp_manager();
|
|
7286
|
+
}
|
|
7287
|
+
});
|
|
7288
|
+
|
|
7289
|
+
// ../skills/dist/built-in/code-sandbox/code-executor.js
|
|
7290
|
+
import { spawn } from "node:child_process";
|
|
7291
|
+
import fs5 from "node:fs";
|
|
7292
|
+
import path7 from "node:path";
|
|
7293
|
+
import os3 from "node:os";
|
|
7294
|
+
import crypto5 from "node:crypto";
|
|
7295
|
+
var CodeExecutor;
|
|
7296
|
+
var init_code_executor = __esm({
|
|
7297
|
+
"../skills/dist/built-in/code-sandbox/code-executor.js"() {
|
|
7298
|
+
"use strict";
|
|
7299
|
+
CodeExecutor = class {
|
|
7300
|
+
async execute(code, language, options) {
|
|
7301
|
+
const timeout = Math.min(options?.timeout ?? 3e4, 12e4);
|
|
7302
|
+
const tmpDir = path7.join(os3.tmpdir(), `alfred-sandbox-${crypto5.randomUUID()}`);
|
|
7303
|
+
fs5.mkdirSync(tmpDir, { recursive: true });
|
|
7304
|
+
try {
|
|
7305
|
+
const ext = language === "javascript" ? "js" : "py";
|
|
7306
|
+
const scriptPath = path7.join(tmpDir, `script.${ext}`);
|
|
7307
|
+
fs5.writeFileSync(scriptPath, code);
|
|
7308
|
+
const cmd = language === "javascript" ? "node" : process.platform === "win32" ? "python" : "python3";
|
|
7309
|
+
const args = [scriptPath];
|
|
7310
|
+
const startTime = Date.now();
|
|
7311
|
+
return await new Promise((resolve) => {
|
|
7312
|
+
const proc = spawn(cmd, args, {
|
|
7313
|
+
cwd: tmpDir,
|
|
7314
|
+
timeout,
|
|
7315
|
+
env: { ...process.env, ...options?.env, TMPDIR: tmpDir, TEMP: tmpDir },
|
|
7316
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
7317
|
+
});
|
|
7318
|
+
let stdout = "";
|
|
7319
|
+
let stderr = "";
|
|
7320
|
+
proc.stdout.on("data", (data) => {
|
|
7321
|
+
stdout += data.toString();
|
|
7322
|
+
});
|
|
7323
|
+
proc.stderr.on("data", (data) => {
|
|
7324
|
+
stderr += data.toString();
|
|
7325
|
+
});
|
|
7326
|
+
proc.on("close", (exitCode) => {
|
|
7327
|
+
const durationMs = Date.now() - startTime;
|
|
7328
|
+
const files = [];
|
|
7329
|
+
try {
|
|
7330
|
+
const outputFiles = fs5.readdirSync(tmpDir).filter((f) => !f.startsWith("script."));
|
|
7331
|
+
for (const f of outputFiles) {
|
|
7332
|
+
const filePath = path7.join(tmpDir, f);
|
|
7333
|
+
const stat = fs5.statSync(filePath);
|
|
7334
|
+
if (stat.isFile() && stat.size < 1e7) {
|
|
7335
|
+
const data = fs5.readFileSync(filePath);
|
|
7336
|
+
const mimeType = f.endsWith(".png") ? "image/png" : f.endsWith(".jpg") || f.endsWith(".jpeg") ? "image/jpeg" : f.endsWith(".svg") ? "image/svg+xml" : f.endsWith(".csv") ? "text/csv" : f.endsWith(".json") ? "application/json" : "application/octet-stream";
|
|
7337
|
+
files.push({ name: f, data, mimeType });
|
|
7338
|
+
}
|
|
7339
|
+
}
|
|
7340
|
+
} catch {
|
|
7341
|
+
}
|
|
7342
|
+
resolve({
|
|
7343
|
+
stdout: stdout.slice(0, 5e4),
|
|
7344
|
+
// Cap output
|
|
7345
|
+
stderr: stderr.slice(0, 1e4),
|
|
7346
|
+
exitCode: exitCode ?? 1,
|
|
7347
|
+
files: files.length > 0 ? files : void 0,
|
|
7348
|
+
durationMs
|
|
7349
|
+
});
|
|
7350
|
+
});
|
|
7351
|
+
proc.on("error", (err) => {
|
|
7352
|
+
resolve({
|
|
7353
|
+
stdout: "",
|
|
7354
|
+
stderr: err.message,
|
|
7355
|
+
exitCode: 1,
|
|
7356
|
+
durationMs: Date.now() - startTime
|
|
7357
|
+
});
|
|
7358
|
+
});
|
|
7359
|
+
proc.stdin.end();
|
|
7360
|
+
});
|
|
7361
|
+
} finally {
|
|
7362
|
+
try {
|
|
7363
|
+
fs5.rmSync(tmpDir, { recursive: true, force: true });
|
|
7364
|
+
} catch {
|
|
7365
|
+
}
|
|
7366
|
+
}
|
|
7367
|
+
}
|
|
7368
|
+
};
|
|
7369
|
+
}
|
|
7370
|
+
});
|
|
7371
|
+
|
|
7372
|
+
// ../skills/dist/built-in/code-sandbox/code-skill.js
|
|
7373
|
+
var CodeExecutionSkill;
|
|
7374
|
+
var init_code_skill = __esm({
|
|
7375
|
+
"../skills/dist/built-in/code-sandbox/code-skill.js"() {
|
|
7376
|
+
"use strict";
|
|
7377
|
+
init_skill();
|
|
7378
|
+
init_code_executor();
|
|
7379
|
+
CodeExecutionSkill = class extends Skill {
|
|
7380
|
+
metadata = {
|
|
7381
|
+
name: "code_sandbox",
|
|
7382
|
+
description: "Execute code in a sandboxed environment. Supports JavaScript (Node.js) and Python. Use for calculations, data processing, generating charts, or testing code snippets. Code runs in an isolated temp directory with a timeout.",
|
|
7383
|
+
riskLevel: "destructive",
|
|
7384
|
+
version: "1.0.0",
|
|
7385
|
+
timeoutMs: 12e4,
|
|
7386
|
+
inputSchema: {
|
|
7387
|
+
type: "object",
|
|
7388
|
+
properties: {
|
|
7389
|
+
action: { type: "string", enum: ["run", "run_with_data"], description: "Action to perform" },
|
|
7390
|
+
code: { type: "string", description: "Code to execute" },
|
|
7391
|
+
language: { type: "string", enum: ["javascript", "python"], description: "Programming language" },
|
|
7392
|
+
data: { type: "string", description: "Input data to pass (available as DATA env var or stdin)" },
|
|
7393
|
+
timeout: { type: "number", description: "Timeout in ms (max 120000)" }
|
|
7394
|
+
},
|
|
7395
|
+
required: ["action", "code", "language"]
|
|
7396
|
+
}
|
|
7397
|
+
};
|
|
7398
|
+
executor = new CodeExecutor();
|
|
7399
|
+
allowedLanguages;
|
|
7400
|
+
maxTimeout;
|
|
7401
|
+
constructor(config) {
|
|
7402
|
+
super();
|
|
7403
|
+
this.allowedLanguages = new Set(config?.allowedLanguages ?? ["javascript", "python"]);
|
|
7404
|
+
this.maxTimeout = config?.maxTimeoutMs ?? 12e4;
|
|
7405
|
+
}
|
|
7406
|
+
async execute(input2, _context) {
|
|
7407
|
+
const action = input2.action;
|
|
7408
|
+
const code = input2.code;
|
|
7409
|
+
const language = input2.language;
|
|
7410
|
+
const data = input2.data;
|
|
7411
|
+
const timeout = Math.min(input2.timeout ?? 3e4, this.maxTimeout);
|
|
7412
|
+
if (!code)
|
|
7413
|
+
return { success: false, error: 'Missing required field "code"' };
|
|
7414
|
+
if (!language)
|
|
7415
|
+
return { success: false, error: 'Missing required field "language"' };
|
|
7416
|
+
if (!this.allowedLanguages.has(language)) {
|
|
7417
|
+
return { success: false, error: `Language "${language}" is not allowed. Allowed: ${[...this.allowedLanguages].join(", ")}` };
|
|
7418
|
+
}
|
|
7419
|
+
let finalCode = code;
|
|
7420
|
+
if (action === "run_with_data" && data) {
|
|
7421
|
+
if (language === "javascript") {
|
|
7422
|
+
finalCode = `const INPUT_DATA = ${JSON.stringify(data)};
|
|
7423
|
+
${code}`;
|
|
7424
|
+
} else {
|
|
7425
|
+
finalCode = `INPUT_DATA = ${JSON.stringify(data)}
|
|
7426
|
+
${code}`;
|
|
7427
|
+
}
|
|
7428
|
+
}
|
|
7429
|
+
const result = await this.executor.execute(finalCode, language, { timeout });
|
|
7430
|
+
const output2 = [
|
|
7431
|
+
result.stdout ? `Output:
|
|
7432
|
+
${result.stdout}` : "",
|
|
7433
|
+
result.stderr ? `Errors:
|
|
7434
|
+
${result.stderr}` : "",
|
|
7435
|
+
`Exit code: ${result.exitCode}`,
|
|
7436
|
+
`Duration: ${result.durationMs}ms`
|
|
7437
|
+
].filter(Boolean).join("\n\n");
|
|
7438
|
+
return {
|
|
7439
|
+
success: result.exitCode === 0,
|
|
7440
|
+
data: {
|
|
7441
|
+
stdout: result.stdout,
|
|
7442
|
+
stderr: result.stderr,
|
|
7443
|
+
exitCode: result.exitCode,
|
|
7444
|
+
durationMs: result.durationMs,
|
|
7445
|
+
fileCount: result.files?.length ?? 0
|
|
7446
|
+
},
|
|
7447
|
+
display: output2,
|
|
7448
|
+
error: result.exitCode !== 0 ? `Code execution failed with exit code ${result.exitCode}` : void 0
|
|
7449
|
+
};
|
|
7450
|
+
}
|
|
7451
|
+
};
|
|
7452
|
+
}
|
|
7453
|
+
});
|
|
7454
|
+
|
|
7455
|
+
// ../skills/dist/built-in/code-sandbox/index.js
|
|
7456
|
+
var init_code_sandbox = __esm({
|
|
7457
|
+
"../skills/dist/built-in/code-sandbox/index.js"() {
|
|
7458
|
+
"use strict";
|
|
7459
|
+
init_code_executor();
|
|
7460
|
+
init_code_skill();
|
|
7461
|
+
}
|
|
7462
|
+
});
|
|
7463
|
+
|
|
7464
|
+
// ../skills/dist/built-in/document.js
|
|
7465
|
+
var DocumentSkill;
|
|
7466
|
+
var init_document = __esm({
|
|
7467
|
+
"../skills/dist/built-in/document.js"() {
|
|
7468
|
+
"use strict";
|
|
7469
|
+
init_skill();
|
|
7470
|
+
DocumentSkill = class extends Skill {
|
|
7471
|
+
docRepo;
|
|
7472
|
+
processor;
|
|
7473
|
+
embeddingService;
|
|
7474
|
+
metadata = {
|
|
7475
|
+
name: "document",
|
|
7476
|
+
description: "Ingest, search, summarize, list, or delete documents. Supports PDF, DOCX, TXT, CSV, and Markdown files. Documents are chunked and embedded for semantic search.",
|
|
7477
|
+
riskLevel: "write",
|
|
7478
|
+
version: "1.0.0",
|
|
7479
|
+
timeoutMs: 12e4,
|
|
7480
|
+
inputSchema: {
|
|
7481
|
+
type: "object",
|
|
7482
|
+
properties: {
|
|
7483
|
+
action: {
|
|
7484
|
+
type: "string",
|
|
7485
|
+
enum: ["ingest", "search", "summarize", "list", "delete"],
|
|
7486
|
+
description: "Action to perform"
|
|
7487
|
+
},
|
|
7488
|
+
file_path: { type: "string", description: "Path to the file (for ingest)" },
|
|
7489
|
+
filename: { type: "string", description: "Original filename (for ingest)" },
|
|
7490
|
+
mime_type: { type: "string", description: "MIME type of the file (for ingest)" },
|
|
7491
|
+
query: { type: "string", description: "Search query (for search)" },
|
|
7492
|
+
document_id: { type: "string", description: "Document ID (for summarize, delete)" },
|
|
7493
|
+
limit: { type: "number", description: "Max results (for search, list)" }
|
|
7494
|
+
},
|
|
7495
|
+
required: ["action"]
|
|
7496
|
+
}
|
|
7497
|
+
};
|
|
7498
|
+
constructor(docRepo, processor, embeddingService) {
|
|
7499
|
+
super();
|
|
7500
|
+
this.docRepo = docRepo;
|
|
7501
|
+
this.processor = processor;
|
|
7502
|
+
this.embeddingService = embeddingService;
|
|
7503
|
+
}
|
|
7504
|
+
async execute(input2, context) {
|
|
7505
|
+
const action = input2.action;
|
|
7506
|
+
switch (action) {
|
|
7507
|
+
case "ingest":
|
|
7508
|
+
return this.ingest(input2, context);
|
|
7509
|
+
case "search":
|
|
7510
|
+
return this.search(input2, context);
|
|
7511
|
+
case "summarize":
|
|
7512
|
+
return this.summarize(input2);
|
|
7513
|
+
case "list":
|
|
7514
|
+
return this.list(input2, context);
|
|
7515
|
+
case "delete":
|
|
7516
|
+
return this.deleteDoc(input2);
|
|
7517
|
+
default:
|
|
7518
|
+
return {
|
|
7519
|
+
success: false,
|
|
7520
|
+
error: `Unknown action: "${String(action)}". Valid actions: ingest, search, summarize, list, delete`
|
|
7521
|
+
};
|
|
7522
|
+
}
|
|
7523
|
+
}
|
|
7524
|
+
async ingest(input2, context) {
|
|
7525
|
+
const filePath = input2.file_path;
|
|
7526
|
+
const filename = input2.filename;
|
|
7527
|
+
const mimeType = input2.mime_type;
|
|
7528
|
+
if (!filePath || typeof filePath !== "string") {
|
|
7529
|
+
return { success: false, error: 'Missing required field "file_path" for ingest action' };
|
|
7530
|
+
}
|
|
7531
|
+
if (!filename || typeof filename !== "string") {
|
|
7532
|
+
return { success: false, error: 'Missing required field "filename" for ingest action' };
|
|
7533
|
+
}
|
|
7534
|
+
if (!mimeType || typeof mimeType !== "string") {
|
|
7535
|
+
return { success: false, error: 'Missing required field "mime_type" for ingest action' };
|
|
7536
|
+
}
|
|
7537
|
+
try {
|
|
7538
|
+
const result = await this.processor.ingest(context.userId, filePath, filename, mimeType);
|
|
7539
|
+
return {
|
|
7540
|
+
success: true,
|
|
7541
|
+
data: result,
|
|
7542
|
+
display: `Document "${filename}" ingested successfully (${result.chunkCount} chunks). ID: ${result.documentId.slice(0, 8)}...`
|
|
7543
|
+
};
|
|
7544
|
+
} catch (err) {
|
|
7545
|
+
return {
|
|
7546
|
+
success: false,
|
|
7547
|
+
error: `Failed to ingest document: ${err instanceof Error ? err.message : String(err)}`
|
|
7548
|
+
};
|
|
7549
|
+
}
|
|
7550
|
+
}
|
|
7551
|
+
async search(input2, context) {
|
|
7552
|
+
const query = input2.query;
|
|
7553
|
+
const limit = input2.limit || 5;
|
|
7554
|
+
if (!query || typeof query !== "string") {
|
|
7555
|
+
return { success: false, error: 'Missing required field "query" for search action' };
|
|
7556
|
+
}
|
|
7557
|
+
if (!this.embeddingService) {
|
|
7558
|
+
return { success: false, error: "Embedding service not available for document search" };
|
|
7559
|
+
}
|
|
7560
|
+
const results = await this.embeddingService.semanticSearch(context.userId, query, limit);
|
|
7561
|
+
const docResults = results.filter((r) => r.category === "document");
|
|
7562
|
+
if (docResults.length === 0) {
|
|
7563
|
+
return { success: true, data: [], display: `No document matches found for "${query}".` };
|
|
7564
|
+
}
|
|
7565
|
+
const display = docResults.map((r, i) => `${i + 1}. (score: ${r.score.toFixed(3)}) ${r.value.slice(0, 200)}${r.value.length > 200 ? "..." : ""}`).join("\n\n");
|
|
7566
|
+
return {
|
|
7567
|
+
success: true,
|
|
7568
|
+
data: docResults,
|
|
7569
|
+
display: `Found ${docResults.length} relevant chunk(s):
|
|
7570
|
+
|
|
7571
|
+
${display}`
|
|
7572
|
+
};
|
|
7573
|
+
}
|
|
7574
|
+
summarize(input2) {
|
|
7575
|
+
const documentId = input2.document_id;
|
|
7576
|
+
if (!documentId || typeof documentId !== "string") {
|
|
7577
|
+
return { success: false, error: 'Missing required field "document_id" for summarize action' };
|
|
7578
|
+
}
|
|
7579
|
+
const doc = this.docRepo.getDocument(documentId);
|
|
7580
|
+
if (!doc) {
|
|
7581
|
+
return { success: false, error: `Document "${documentId}" not found` };
|
|
7582
|
+
}
|
|
7583
|
+
const chunks = this.docRepo.getChunks(documentId);
|
|
7584
|
+
if (chunks.length === 0) {
|
|
7585
|
+
return { success: true, data: { document: doc, content: "" }, display: `Document "${doc.filename}" has no content chunks.` };
|
|
7586
|
+
}
|
|
7587
|
+
const fullContent = chunks.map((c) => c.content).join("\n\n");
|
|
7588
|
+
const maxChars = 8e3;
|
|
7589
|
+
const truncated = fullContent.length > maxChars;
|
|
7590
|
+
const content = truncated ? fullContent.slice(0, maxChars) + "\n\n[... truncated]" : fullContent;
|
|
7591
|
+
return {
|
|
7592
|
+
success: true,
|
|
7593
|
+
data: {
|
|
7594
|
+
document: doc,
|
|
7595
|
+
content,
|
|
7596
|
+
totalChunks: chunks.length,
|
|
7597
|
+
truncated
|
|
7598
|
+
},
|
|
7599
|
+
display: `Document: **${doc.filename}** (${chunks.length} chunks, ${doc.sizeBytes} bytes)
|
|
7600
|
+
|
|
7601
|
+
${content}`
|
|
7602
|
+
};
|
|
7603
|
+
}
|
|
7604
|
+
list(input2, context) {
|
|
7605
|
+
const limit = input2.limit || 50;
|
|
7606
|
+
const docs = this.docRepo.listByUser(context.userId);
|
|
7607
|
+
const limited = docs.slice(0, limit);
|
|
7608
|
+
if (limited.length === 0) {
|
|
7609
|
+
return { success: true, data: [], display: "No documents found." };
|
|
7610
|
+
}
|
|
7611
|
+
const display = limited.map((d) => `- **${d.filename}** (${d.id.slice(0, 8)}...) \u2014 ${d.mimeType}, ${d.chunkCount} chunks, ${d.sizeBytes} bytes`).join("\n");
|
|
7612
|
+
return {
|
|
7613
|
+
success: true,
|
|
7614
|
+
data: limited,
|
|
7615
|
+
display: `${limited.length} document(s):
|
|
7616
|
+
${display}`
|
|
7617
|
+
};
|
|
7618
|
+
}
|
|
7619
|
+
deleteDoc(input2) {
|
|
7620
|
+
const documentId = input2.document_id;
|
|
7621
|
+
if (!documentId || typeof documentId !== "string") {
|
|
7622
|
+
return { success: false, error: 'Missing required field "document_id" for delete action' };
|
|
7623
|
+
}
|
|
7624
|
+
const doc = this.docRepo.getDocument(documentId);
|
|
7625
|
+
if (!doc) {
|
|
7626
|
+
return { success: false, error: `Document "${documentId}" not found` };
|
|
7627
|
+
}
|
|
7628
|
+
this.docRepo.deleteDocument(documentId);
|
|
7629
|
+
return {
|
|
7630
|
+
success: true,
|
|
7631
|
+
data: { documentId },
|
|
7632
|
+
display: `Document "${doc.filename}" deleted.`
|
|
7633
|
+
};
|
|
7634
|
+
}
|
|
7635
|
+
};
|
|
7636
|
+
}
|
|
7637
|
+
});
|
|
7638
|
+
|
|
7639
|
+
// ../skills/dist/index.js
|
|
7640
|
+
var dist_exports = {};
|
|
7641
|
+
__export(dist_exports, {
|
|
7642
|
+
ActivityTracker: () => ActivityTracker,
|
|
7643
|
+
BackgroundTaskSkill: () => BackgroundTaskSkill,
|
|
7644
|
+
BrowserSkill: () => BrowserSkill,
|
|
7645
|
+
CalculatorSkill: () => CalculatorSkill,
|
|
7646
|
+
CalendarProvider: () => CalendarProvider,
|
|
7647
|
+
CalendarSkill: () => CalendarSkill,
|
|
7648
|
+
ClipboardSkill: () => ClipboardSkill,
|
|
7649
|
+
CodeExecutionSkill: () => CodeExecutionSkill,
|
|
7650
|
+
CodeExecutor: () => CodeExecutor,
|
|
7651
|
+
CrossPlatformSkill: () => CrossPlatformSkill,
|
|
7652
|
+
DelegateSkill: () => DelegateSkill,
|
|
7653
|
+
DocumentSkill: () => DocumentSkill,
|
|
7654
|
+
EmailSkill: () => EmailSkill,
|
|
7655
|
+
FileSkill: () => FileSkill,
|
|
7656
|
+
HttpSkill: () => HttpSkill,
|
|
7657
|
+
MCPClient: () => MCPClient,
|
|
7658
|
+
MCPManager: () => MCPManager,
|
|
7659
|
+
MCPSkillAdapter: () => MCPSkillAdapter,
|
|
7660
|
+
MemorySkill: () => MemorySkill,
|
|
7661
|
+
NoteSkill: () => NoteSkill,
|
|
7662
|
+
PluginLoader: () => PluginLoader,
|
|
7663
|
+
ProfileSkill: () => ProfileSkill,
|
|
7664
|
+
ReminderSkill: () => ReminderSkill,
|
|
7665
|
+
ScheduledTaskSkill: () => ScheduledTaskSkill,
|
|
7666
|
+
ScreenshotSkill: () => ScreenshotSkill,
|
|
7667
|
+
ShellSkill: () => ShellSkill,
|
|
7668
|
+
Skill: () => Skill,
|
|
7669
|
+
SkillRegistry: () => SkillRegistry,
|
|
7670
|
+
SkillSandbox: () => SkillSandbox,
|
|
7671
|
+
SystemInfoSkill: () => SystemInfoSkill,
|
|
7672
|
+
WeatherSkill: () => WeatherSkill,
|
|
7673
|
+
WebSearchSkill: () => WebSearchSkill,
|
|
7674
|
+
createCalendarProvider: () => createCalendarProvider
|
|
7675
|
+
});
|
|
7676
|
+
var init_dist6 = __esm({
|
|
7677
|
+
"../skills/dist/index.js"() {
|
|
7678
|
+
"use strict";
|
|
7679
|
+
init_skill();
|
|
7680
|
+
init_skill_registry();
|
|
7681
|
+
init_skill_sandbox();
|
|
7682
|
+
init_activity_tracker();
|
|
7683
|
+
init_plugin_loader();
|
|
7684
|
+
init_calculator();
|
|
7685
|
+
init_system_info();
|
|
7686
|
+
init_web_search();
|
|
7687
|
+
init_reminder();
|
|
7688
|
+
init_note();
|
|
7689
|
+
init_weather();
|
|
7690
|
+
init_shell();
|
|
6021
7691
|
init_memory();
|
|
6022
7692
|
init_delegate();
|
|
6023
7693
|
init_email();
|
|
@@ -6028,6 +7698,12 @@ var init_dist6 = __esm({
|
|
|
6028
7698
|
init_browser();
|
|
6029
7699
|
init_profile();
|
|
6030
7700
|
init_calendar();
|
|
7701
|
+
init_cross_platform();
|
|
7702
|
+
init_background_task();
|
|
7703
|
+
init_scheduled_task();
|
|
7704
|
+
init_mcp();
|
|
7705
|
+
init_code_sandbox();
|
|
7706
|
+
init_document();
|
|
6031
7707
|
}
|
|
6032
7708
|
});
|
|
6033
7709
|
|
|
@@ -6060,8 +7736,8 @@ var init_conversation_manager = __esm({
|
|
|
6060
7736
|
});
|
|
6061
7737
|
|
|
6062
7738
|
// ../core/dist/message-pipeline.js
|
|
6063
|
-
import
|
|
6064
|
-
import
|
|
7739
|
+
import fs6 from "node:fs";
|
|
7740
|
+
import path8 from "node:path";
|
|
6065
7741
|
var MAX_TOOL_ITERATIONS, TOKEN_BUDGET_RATIO, MAX_INLINE_FILE_SIZE, MessagePipeline;
|
|
6066
7742
|
var init_message_pipeline = __esm({
|
|
6067
7743
|
"../core/dist/message-pipeline.js"() {
|
|
@@ -6105,6 +7781,7 @@ var init_message_pipeline = __esm({
|
|
|
6105
7781
|
this.logger.info({ platform: message.platform, userId: message.userId, chatId: message.chatId }, "Processing message");
|
|
6106
7782
|
try {
|
|
6107
7783
|
const user = this.users.findOrCreate(message.platform, message.userId, message.userName, message.displayName);
|
|
7784
|
+
const masterUserId = this.users.getMasterUserId?.(user.id) ?? user.id;
|
|
6108
7785
|
const conversation = this.conversationManager.getOrCreateConversation(message.platform, message.chatId, user.id);
|
|
6109
7786
|
const history = this.conversationManager.getHistory(conversation.id, 50);
|
|
6110
7787
|
this.conversationManager.addMessage(conversation.id, "user", message.text);
|
|
@@ -6112,8 +7789,8 @@ var init_message_pipeline = __esm({
|
|
|
6112
7789
|
if (this.memoryRepo) {
|
|
6113
7790
|
try {
|
|
6114
7791
|
if (this.embeddingService && message.text) {
|
|
6115
|
-
const semanticResults = await this.embeddingService.semanticSearch(
|
|
6116
|
-
const recentResults = this.memoryRepo.getRecentForPrompt(
|
|
7792
|
+
const semanticResults = await this.embeddingService.semanticSearch(masterUserId, message.text, 10);
|
|
7793
|
+
const recentResults = this.memoryRepo.getRecentForPrompt(masterUserId, 5);
|
|
6117
7794
|
const seen = /* @__PURE__ */ new Set();
|
|
6118
7795
|
memories = [];
|
|
6119
7796
|
for (const m of semanticResults) {
|
|
@@ -6129,7 +7806,7 @@ var init_message_pipeline = __esm({
|
|
|
6129
7806
|
}
|
|
6130
7807
|
}
|
|
6131
7808
|
} else {
|
|
6132
|
-
memories = this.memoryRepo.getRecentForPrompt(
|
|
7809
|
+
memories = this.memoryRepo.getRecentForPrompt(masterUserId, 20);
|
|
6133
7810
|
}
|
|
6134
7811
|
} catch {
|
|
6135
7812
|
}
|
|
@@ -6190,25 +7867,14 @@ var init_message_pipeline = __esm({
|
|
|
6190
7867
|
});
|
|
6191
7868
|
}
|
|
6192
7869
|
messages.push({ role: "assistant", content: assistantContent });
|
|
6193
|
-
const toolResultBlocks =
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
platform: message.platform,
|
|
6202
|
-
conversationId: conversation.id,
|
|
6203
|
-
timezone: resolvedTimezone
|
|
6204
|
-
}, onProgress);
|
|
6205
|
-
toolResultBlocks.push({
|
|
6206
|
-
type: "tool_result",
|
|
6207
|
-
tool_use_id: toolCall.id,
|
|
6208
|
-
content: result.content,
|
|
6209
|
-
is_error: result.isError
|
|
6210
|
-
});
|
|
6211
|
-
}
|
|
7870
|
+
const toolResultBlocks = await this.executeToolCallsParallel(response.toolCalls, {
|
|
7871
|
+
userId: message.userId,
|
|
7872
|
+
chatId: message.chatId,
|
|
7873
|
+
chatType: message.chatType,
|
|
7874
|
+
platform: message.platform,
|
|
7875
|
+
conversationId: conversation.id,
|
|
7876
|
+
timezone: resolvedTimezone
|
|
7877
|
+
}, onProgress);
|
|
6212
7878
|
const toolCallSummary = response.toolCalls.map((tc) => `[Used ${tc.name}: ${JSON.stringify(tc.input)}]`).join("\n");
|
|
6213
7879
|
const toolResultSummary = toolResultBlocks.map((tr) => {
|
|
6214
7880
|
const output2 = tr.type === "tool_result" ? String(tr.content).slice(0, 1e3) : "";
|
|
@@ -6231,6 +7897,40 @@ var init_message_pipeline = __esm({
|
|
|
6231
7897
|
throw error;
|
|
6232
7898
|
}
|
|
6233
7899
|
}
|
|
7900
|
+
async executeToolCallsParallel(toolCalls, context, onProgress) {
|
|
7901
|
+
if (toolCalls.length === 1) {
|
|
7902
|
+
const tc = toolCalls[0];
|
|
7903
|
+
const toolLabel = this.getToolLabel(tc.name, tc.input);
|
|
7904
|
+
onProgress?.(toolLabel);
|
|
7905
|
+
const result = await this.executeToolCall(tc, context, onProgress);
|
|
7906
|
+
return [{
|
|
7907
|
+
type: "tool_result",
|
|
7908
|
+
tool_use_id: tc.id,
|
|
7909
|
+
content: result.content,
|
|
7910
|
+
is_error: result.isError
|
|
7911
|
+
}];
|
|
7912
|
+
}
|
|
7913
|
+
onProgress?.(`Running ${toolCalls.length} tools in parallel...`);
|
|
7914
|
+
const results = await Promise.allSettled(toolCalls.map((tc) => this.executeToolCall(tc, context, onProgress)));
|
|
7915
|
+
return toolCalls.map((tc, i) => {
|
|
7916
|
+
const settled = results[i];
|
|
7917
|
+
if (settled.status === "fulfilled") {
|
|
7918
|
+
return {
|
|
7919
|
+
type: "tool_result",
|
|
7920
|
+
tool_use_id: tc.id,
|
|
7921
|
+
content: settled.value.content,
|
|
7922
|
+
is_error: settled.value.isError
|
|
7923
|
+
};
|
|
7924
|
+
} else {
|
|
7925
|
+
return {
|
|
7926
|
+
type: "tool_result",
|
|
7927
|
+
tool_use_id: tc.id,
|
|
7928
|
+
content: `Tool execution failed: ${settled.reason}`,
|
|
7929
|
+
is_error: true
|
|
7930
|
+
};
|
|
7931
|
+
}
|
|
7932
|
+
});
|
|
7933
|
+
}
|
|
6234
7934
|
async executeToolCall(toolCall, context, onProgress) {
|
|
6235
7935
|
const skill = this.skillRegistry?.get(toolCall.name);
|
|
6236
7936
|
if (!skill) {
|
|
@@ -6257,12 +7957,9 @@ var init_message_pipeline = __esm({
|
|
|
6257
7957
|
if (this.skillSandbox) {
|
|
6258
7958
|
let tracker;
|
|
6259
7959
|
let agentId;
|
|
6260
|
-
if (toolCall.name === "delegate"
|
|
6261
|
-
const
|
|
6262
|
-
|
|
6263
|
-
delegateSkill.setProgressCallback(onProgress);
|
|
6264
|
-
}
|
|
6265
|
-
tracker = delegateSkill.createTracker();
|
|
7960
|
+
if (toolCall.name === "delegate") {
|
|
7961
|
+
const { ActivityTracker: ActivityTracker2 } = await Promise.resolve().then(() => (init_dist6(), dist_exports));
|
|
7962
|
+
tracker = new ActivityTracker2(onProgress);
|
|
6266
7963
|
agentId = `agent-${++this.agentIdCounter}`;
|
|
6267
7964
|
this.activeAgents.set(agentId, {
|
|
6268
7965
|
chatId: context.chatId,
|
|
@@ -6271,8 +7968,9 @@ var init_message_pipeline = __esm({
|
|
|
6271
7968
|
startedAt: Date.now()
|
|
6272
7969
|
});
|
|
6273
7970
|
}
|
|
7971
|
+
const execContext = toolCall.name === "delegate" ? { ...context, tracker, onProgress } : context;
|
|
6274
7972
|
try {
|
|
6275
|
-
const result = await this.skillSandbox.execute(skill, toolCall.input,
|
|
7973
|
+
const result = await this.skillSandbox.execute(skill, toolCall.input, execContext, void 0, tracker);
|
|
6276
7974
|
return {
|
|
6277
7975
|
content: result.display ?? (result.success ? JSON.stringify(result.data) : result.error ?? "Unknown error"),
|
|
6278
7976
|
isError: !result.success
|
|
@@ -6330,6 +8028,16 @@ var init_message_pipeline = __esm({
|
|
|
6330
8028
|
return `Profile: ${String(input2.action ?? "")}`;
|
|
6331
8029
|
case "calendar":
|
|
6332
8030
|
return `Calendar: ${String(input2.action ?? "")}`;
|
|
8031
|
+
case "background_task":
|
|
8032
|
+
return `Background task: ${String(input2.action ?? "")}`;
|
|
8033
|
+
case "scheduled_task":
|
|
8034
|
+
return `Scheduled task: ${String(input2.action ?? "")}`;
|
|
8035
|
+
case "cross_platform":
|
|
8036
|
+
return `Cross-platform: ${String(input2.action ?? "")}`;
|
|
8037
|
+
case "code_sandbox":
|
|
8038
|
+
return `Running code...`;
|
|
8039
|
+
case "document":
|
|
8040
|
+
return `Document: ${String(input2.action ?? "")}`;
|
|
6333
8041
|
default:
|
|
6334
8042
|
return `Using ${toolName}...`;
|
|
6335
8043
|
}
|
|
@@ -6472,9 +8180,9 @@ ${textContent}`;
|
|
|
6472
8180
|
saveToInbox(attachment) {
|
|
6473
8181
|
if (!attachment.data)
|
|
6474
8182
|
return void 0;
|
|
6475
|
-
const inboxDir = this.inboxPath ??
|
|
8183
|
+
const inboxDir = this.inboxPath ?? path8.resolve("./data/inbox");
|
|
6476
8184
|
try {
|
|
6477
|
-
|
|
8185
|
+
fs6.mkdirSync(inboxDir, { recursive: true });
|
|
6478
8186
|
} catch {
|
|
6479
8187
|
this.logger.error({ inboxDir }, "Cannot create inbox directory");
|
|
6480
8188
|
return void 0;
|
|
@@ -6483,9 +8191,9 @@ ${textContent}`;
|
|
|
6483
8191
|
const originalName = attachment.fileName ?? `file_${timestamp}`;
|
|
6484
8192
|
const safeName = originalName.replace(/[<>:"/\\|?*]/g, "_");
|
|
6485
8193
|
const fileName = `${timestamp}_${safeName}`;
|
|
6486
|
-
const filePath =
|
|
8194
|
+
const filePath = path8.join(inboxDir, fileName);
|
|
6487
8195
|
try {
|
|
6488
|
-
|
|
8196
|
+
fs6.writeFileSync(filePath, attachment.data);
|
|
6489
8197
|
return filePath;
|
|
6490
8198
|
} catch (err) {
|
|
6491
8199
|
this.logger.error({ err, filePath }, "Failed to save file to inbox");
|
|
@@ -6775,6 +8483,354 @@ var init_embedding_service = __esm({
|
|
|
6775
8483
|
}
|
|
6776
8484
|
});
|
|
6777
8485
|
|
|
8486
|
+
// ../core/dist/document-processor.js
|
|
8487
|
+
var DocumentProcessor;
|
|
8488
|
+
var init_document_processor = __esm({
|
|
8489
|
+
"../core/dist/document-processor.js"() {
|
|
8490
|
+
"use strict";
|
|
8491
|
+
DocumentProcessor = class {
|
|
8492
|
+
docRepo;
|
|
8493
|
+
embeddingService;
|
|
8494
|
+
logger;
|
|
8495
|
+
constructor(docRepo, embeddingService, logger) {
|
|
8496
|
+
this.docRepo = docRepo;
|
|
8497
|
+
this.embeddingService = embeddingService;
|
|
8498
|
+
this.logger = logger;
|
|
8499
|
+
}
|
|
8500
|
+
async ingest(userId, filePath, filename, mimeType) {
|
|
8501
|
+
const content = await this.extractText(filePath, mimeType);
|
|
8502
|
+
const fs12 = await import("node:fs");
|
|
8503
|
+
const stat = fs12.statSync(filePath);
|
|
8504
|
+
const doc = this.docRepo.createDocument(userId, filename, mimeType, stat.size);
|
|
8505
|
+
const chunks = this.chunkText(content, 500, 50);
|
|
8506
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
8507
|
+
let embeddingId;
|
|
8508
|
+
try {
|
|
8509
|
+
await this.embeddingService.embedAndStore(userId, chunks[i], "document", `${doc.id}:${i}`);
|
|
8510
|
+
embeddingId = `${doc.id}:${i}`;
|
|
8511
|
+
} catch {
|
|
8512
|
+
this.logger.warn({ documentId: doc.id, chunkIndex: i }, "Embedding failed for chunk, continuing");
|
|
8513
|
+
}
|
|
8514
|
+
this.docRepo.addChunk(doc.id, i, chunks[i], embeddingId);
|
|
8515
|
+
}
|
|
8516
|
+
this.docRepo.updateChunkCount(doc.id, chunks.length);
|
|
8517
|
+
this.logger.info({ documentId: doc.id, filename, chunkCount: chunks.length }, "Document ingested");
|
|
8518
|
+
return { documentId: doc.id, chunkCount: chunks.length };
|
|
8519
|
+
}
|
|
8520
|
+
async extractText(filePath, mimeType) {
|
|
8521
|
+
const fs12 = await import("node:fs");
|
|
8522
|
+
if (mimeType === "application/pdf") {
|
|
8523
|
+
try {
|
|
8524
|
+
const pdfParse = (await import("pdf-parse")).default;
|
|
8525
|
+
const buffer = fs12.readFileSync(filePath);
|
|
8526
|
+
const data = await pdfParse(buffer);
|
|
8527
|
+
return data.text;
|
|
8528
|
+
} catch (err) {
|
|
8529
|
+
this.logger.error({ err }, "PDF parsing failed");
|
|
8530
|
+
throw new Error("Failed to parse PDF");
|
|
8531
|
+
}
|
|
8532
|
+
}
|
|
8533
|
+
if (mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document" || mimeType === "application/msword") {
|
|
8534
|
+
try {
|
|
8535
|
+
const mammoth = await import("mammoth");
|
|
8536
|
+
const result = await mammoth.extractRawText({ path: filePath });
|
|
8537
|
+
return result.value;
|
|
8538
|
+
} catch (err) {
|
|
8539
|
+
this.logger.error({ err }, "DOCX parsing failed");
|
|
8540
|
+
throw new Error("Failed to parse DOCX");
|
|
8541
|
+
}
|
|
8542
|
+
}
|
|
8543
|
+
return fs12.readFileSync(filePath, "utf-8");
|
|
8544
|
+
}
|
|
8545
|
+
chunkText(text, targetTokens, overlapTokens) {
|
|
8546
|
+
const charsPerToken = 3.5;
|
|
8547
|
+
const targetChars = Math.round(targetTokens * charsPerToken);
|
|
8548
|
+
const overlapChars = Math.round(overlapTokens * charsPerToken);
|
|
8549
|
+
const chunks = [];
|
|
8550
|
+
let start = 0;
|
|
8551
|
+
while (start < text.length) {
|
|
8552
|
+
let end = start + targetChars;
|
|
8553
|
+
if (end >= text.length) {
|
|
8554
|
+
chunks.push(text.slice(start).trim());
|
|
8555
|
+
break;
|
|
8556
|
+
}
|
|
8557
|
+
const searchStart = Math.max(end - 200, start);
|
|
8558
|
+
const searchRegion = text.slice(searchStart, end + 200);
|
|
8559
|
+
const paragraphBreak = searchRegion.lastIndexOf("\n\n");
|
|
8560
|
+
if (paragraphBreak > 0) {
|
|
8561
|
+
end = searchStart + paragraphBreak;
|
|
8562
|
+
} else {
|
|
8563
|
+
const sentenceBreak = searchRegion.lastIndexOf(". ");
|
|
8564
|
+
if (sentenceBreak > 0) {
|
|
8565
|
+
end = searchStart + sentenceBreak + 1;
|
|
8566
|
+
}
|
|
8567
|
+
}
|
|
8568
|
+
const chunk = text.slice(start, end).trim();
|
|
8569
|
+
if (chunk)
|
|
8570
|
+
chunks.push(chunk);
|
|
8571
|
+
start = end - overlapChars;
|
|
8572
|
+
}
|
|
8573
|
+
return chunks.filter((c) => c.length > 0);
|
|
8574
|
+
}
|
|
8575
|
+
};
|
|
8576
|
+
}
|
|
8577
|
+
});
|
|
8578
|
+
|
|
8579
|
+
// ../core/dist/background-task-runner.js
|
|
8580
|
+
var BackgroundTaskRunner;
|
|
8581
|
+
var init_background_task_runner = __esm({
|
|
8582
|
+
"../core/dist/background-task-runner.js"() {
|
|
8583
|
+
"use strict";
|
|
8584
|
+
BackgroundTaskRunner = class {
|
|
8585
|
+
skillRegistry;
|
|
8586
|
+
skillSandbox;
|
|
8587
|
+
taskRepo;
|
|
8588
|
+
adapters;
|
|
8589
|
+
logger;
|
|
8590
|
+
pollTimer;
|
|
8591
|
+
running = 0;
|
|
8592
|
+
maxConcurrent = 3;
|
|
8593
|
+
pollIntervalMs = 5e3;
|
|
8594
|
+
constructor(skillRegistry, skillSandbox, taskRepo, adapters, logger) {
|
|
8595
|
+
this.skillRegistry = skillRegistry;
|
|
8596
|
+
this.skillSandbox = skillSandbox;
|
|
8597
|
+
this.taskRepo = taskRepo;
|
|
8598
|
+
this.adapters = adapters;
|
|
8599
|
+
this.logger = logger;
|
|
8600
|
+
}
|
|
8601
|
+
start() {
|
|
8602
|
+
this.pollTimer = setInterval(() => this.poll(), this.pollIntervalMs);
|
|
8603
|
+
this.logger.info("Background task runner started");
|
|
8604
|
+
}
|
|
8605
|
+
stop() {
|
|
8606
|
+
if (this.pollTimer) {
|
|
8607
|
+
clearInterval(this.pollTimer);
|
|
8608
|
+
this.pollTimer = void 0;
|
|
8609
|
+
}
|
|
8610
|
+
this.logger.info("Background task runner stopped");
|
|
8611
|
+
}
|
|
8612
|
+
async poll() {
|
|
8613
|
+
if (this.running >= this.maxConcurrent)
|
|
8614
|
+
return;
|
|
8615
|
+
try {
|
|
8616
|
+
const available = this.maxConcurrent - this.running;
|
|
8617
|
+
const tasks = this.taskRepo.getPending(available);
|
|
8618
|
+
for (const task of tasks) {
|
|
8619
|
+
this.running++;
|
|
8620
|
+
this.runTask(task).finally(() => {
|
|
8621
|
+
this.running--;
|
|
8622
|
+
});
|
|
8623
|
+
}
|
|
8624
|
+
} catch (err) {
|
|
8625
|
+
this.logger.error({ err }, "Error polling for background tasks");
|
|
8626
|
+
}
|
|
8627
|
+
}
|
|
8628
|
+
async runTask(task) {
|
|
8629
|
+
this.taskRepo.updateStatus(task.id, "running");
|
|
8630
|
+
try {
|
|
8631
|
+
const skill = this.skillRegistry.get(task.skillName);
|
|
8632
|
+
if (!skill) {
|
|
8633
|
+
this.taskRepo.updateStatus(task.id, "failed", void 0, `Unknown skill: ${task.skillName}`);
|
|
8634
|
+
return;
|
|
8635
|
+
}
|
|
8636
|
+
const input2 = JSON.parse(task.skillInput);
|
|
8637
|
+
const context = {
|
|
8638
|
+
userId: task.userId,
|
|
8639
|
+
chatId: task.chatId,
|
|
8640
|
+
platform: task.platform,
|
|
8641
|
+
conversationId: "",
|
|
8642
|
+
chatType: "dm"
|
|
8643
|
+
};
|
|
8644
|
+
const result = await this.skillSandbox.execute(skill, input2, context);
|
|
8645
|
+
const resultJson = JSON.stringify(result.data ?? result.display ?? result.error);
|
|
8646
|
+
this.taskRepo.updateStatus(task.id, result.success ? "completed" : "failed", resultJson, result.error);
|
|
8647
|
+
const adapter = this.adapters.get(task.platform);
|
|
8648
|
+
if (adapter) {
|
|
8649
|
+
const message = result.success ? `\u2705 Background task completed: ${task.description}
|
|
8650
|
+
|
|
8651
|
+
Result: ${result.display ?? JSON.stringify(result.data)}` : `\u274C Background task failed: ${task.description}
|
|
8652
|
+
|
|
8653
|
+
Error: ${result.error}`;
|
|
8654
|
+
await adapter.sendMessage(task.chatId, message);
|
|
8655
|
+
}
|
|
8656
|
+
} catch (err) {
|
|
8657
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
8658
|
+
this.taskRepo.updateStatus(task.id, "failed", void 0, errorMsg);
|
|
8659
|
+
this.logger.error({ taskId: task.id, err }, "Background task failed");
|
|
8660
|
+
const adapter = this.adapters.get(task.platform);
|
|
8661
|
+
if (adapter) {
|
|
8662
|
+
await adapter.sendMessage(task.chatId, `\u274C Background task failed: ${task.description}
|
|
8663
|
+
|
|
8664
|
+
Error: ${errorMsg}`);
|
|
8665
|
+
}
|
|
8666
|
+
}
|
|
8667
|
+
}
|
|
8668
|
+
};
|
|
8669
|
+
}
|
|
8670
|
+
});
|
|
8671
|
+
|
|
8672
|
+
// ../core/dist/proactive-scheduler.js
|
|
8673
|
+
var ProactiveScheduler;
|
|
8674
|
+
var init_proactive_scheduler = __esm({
|
|
8675
|
+
"../core/dist/proactive-scheduler.js"() {
|
|
8676
|
+
"use strict";
|
|
8677
|
+
ProactiveScheduler = class {
|
|
8678
|
+
actionRepo;
|
|
8679
|
+
skillRegistry;
|
|
8680
|
+
skillSandbox;
|
|
8681
|
+
llm;
|
|
8682
|
+
adapters;
|
|
8683
|
+
logger;
|
|
8684
|
+
tickTimer;
|
|
8685
|
+
tickIntervalMs = 6e4;
|
|
8686
|
+
constructor(actionRepo, skillRegistry, skillSandbox, llm, adapters, logger) {
|
|
8687
|
+
this.actionRepo = actionRepo;
|
|
8688
|
+
this.skillRegistry = skillRegistry;
|
|
8689
|
+
this.skillSandbox = skillSandbox;
|
|
8690
|
+
this.llm = llm;
|
|
8691
|
+
this.adapters = adapters;
|
|
8692
|
+
this.logger = logger;
|
|
8693
|
+
}
|
|
8694
|
+
start() {
|
|
8695
|
+
this.tickTimer = setInterval(() => this.tick(), this.tickIntervalMs);
|
|
8696
|
+
this.logger.info("Proactive scheduler started");
|
|
8697
|
+
}
|
|
8698
|
+
stop() {
|
|
8699
|
+
if (this.tickTimer) {
|
|
8700
|
+
clearInterval(this.tickTimer);
|
|
8701
|
+
this.tickTimer = void 0;
|
|
8702
|
+
}
|
|
8703
|
+
this.logger.info("Proactive scheduler stopped");
|
|
8704
|
+
}
|
|
8705
|
+
async tick() {
|
|
8706
|
+
try {
|
|
8707
|
+
const dueActions = this.actionRepo.getDue();
|
|
8708
|
+
for (const action of dueActions) {
|
|
8709
|
+
try {
|
|
8710
|
+
await this.executeAction(action);
|
|
8711
|
+
} catch (err) {
|
|
8712
|
+
this.logger.error({ err, actionId: action.id }, "Failed to execute scheduled action");
|
|
8713
|
+
}
|
|
8714
|
+
}
|
|
8715
|
+
} catch (err) {
|
|
8716
|
+
this.logger.error({ err }, "Error during proactive scheduler tick");
|
|
8717
|
+
}
|
|
8718
|
+
}
|
|
8719
|
+
async executeAction(action) {
|
|
8720
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8721
|
+
this.logger.info({ actionId: action.id, name: action.name }, "Executing scheduled action");
|
|
8722
|
+
let resultText;
|
|
8723
|
+
if (action.promptTemplate) {
|
|
8724
|
+
try {
|
|
8725
|
+
const response = await this.llm.complete({
|
|
8726
|
+
messages: [{ role: "user", content: action.promptTemplate }],
|
|
8727
|
+
maxTokens: 1024
|
|
8728
|
+
});
|
|
8729
|
+
resultText = response.content;
|
|
8730
|
+
} catch (err) {
|
|
8731
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
8732
|
+
this.logger.error({ actionId: action.id, err }, "LLM call failed for scheduled action");
|
|
8733
|
+
resultText = `Scheduled action "${action.name}" failed: ${errorMsg}`;
|
|
8734
|
+
}
|
|
8735
|
+
} else {
|
|
8736
|
+
const skill = this.skillRegistry.get(action.skillName);
|
|
8737
|
+
if (!skill) {
|
|
8738
|
+
this.logger.warn({ actionId: action.id, skillName: action.skillName }, "Unknown skill for scheduled action");
|
|
8739
|
+
resultText = `Scheduled action "${action.name}" failed: unknown skill "${action.skillName}"`;
|
|
8740
|
+
} else {
|
|
8741
|
+
try {
|
|
8742
|
+
const input2 = JSON.parse(action.skillInput);
|
|
8743
|
+
const context = {
|
|
8744
|
+
userId: action.userId,
|
|
8745
|
+
chatId: action.chatId,
|
|
8746
|
+
platform: action.platform,
|
|
8747
|
+
conversationId: "",
|
|
8748
|
+
chatType: "dm"
|
|
8749
|
+
};
|
|
8750
|
+
const result = await this.skillSandbox.execute(skill, input2, context);
|
|
8751
|
+
resultText = result.success ? `\u{1F514} Scheduled: ${action.name}
|
|
8752
|
+
|
|
8753
|
+
${result.display ?? JSON.stringify(result.data)}` : `\u274C Scheduled action "${action.name}" failed: ${result.error}`;
|
|
8754
|
+
} catch (err) {
|
|
8755
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
8756
|
+
resultText = `\u274C Scheduled action "${action.name}" failed: ${errorMsg}`;
|
|
8757
|
+
}
|
|
8758
|
+
}
|
|
8759
|
+
}
|
|
8760
|
+
const adapter = this.adapters.get(action.platform);
|
|
8761
|
+
if (adapter) {
|
|
8762
|
+
try {
|
|
8763
|
+
await adapter.sendMessage(action.chatId, resultText);
|
|
8764
|
+
} catch (err) {
|
|
8765
|
+
this.logger.error({ err, actionId: action.id }, "Failed to send scheduled action result");
|
|
8766
|
+
}
|
|
8767
|
+
}
|
|
8768
|
+
const nextRunAt = this.calculateNextRun(action);
|
|
8769
|
+
if (nextRunAt) {
|
|
8770
|
+
this.actionRepo.updateLastRun(action.id, now, nextRunAt);
|
|
8771
|
+
} else {
|
|
8772
|
+
this.actionRepo.updateLastRun(action.id, now, null);
|
|
8773
|
+
this.actionRepo.setEnabled(action.id, false);
|
|
8774
|
+
}
|
|
8775
|
+
}
|
|
8776
|
+
calculateNextRun(action) {
|
|
8777
|
+
const now = /* @__PURE__ */ new Date();
|
|
8778
|
+
switch (action.scheduleType) {
|
|
8779
|
+
case "interval": {
|
|
8780
|
+
const minutes = parseInt(action.scheduleValue, 10);
|
|
8781
|
+
if (isNaN(minutes) || minutes <= 0)
|
|
8782
|
+
return null;
|
|
8783
|
+
return new Date(now.getTime() + minutes * 6e4).toISOString();
|
|
8784
|
+
}
|
|
8785
|
+
case "once": {
|
|
8786
|
+
return null;
|
|
8787
|
+
}
|
|
8788
|
+
case "cron": {
|
|
8789
|
+
return this.getNextCronDate(action.scheduleValue, now)?.toISOString() ?? null;
|
|
8790
|
+
}
|
|
8791
|
+
default:
|
|
8792
|
+
return null;
|
|
8793
|
+
}
|
|
8794
|
+
}
|
|
8795
|
+
getNextCronDate(cronExpr, after) {
|
|
8796
|
+
const parts = cronExpr.trim().split(/\s+/);
|
|
8797
|
+
if (parts.length !== 5)
|
|
8798
|
+
return null;
|
|
8799
|
+
const candidate = new Date(after.getTime() + 6e4);
|
|
8800
|
+
candidate.setSeconds(0, 0);
|
|
8801
|
+
for (let i = 0; i < 1440; i++) {
|
|
8802
|
+
if (this.matchesCron(parts, candidate)) {
|
|
8803
|
+
return candidate;
|
|
8804
|
+
}
|
|
8805
|
+
candidate.setTime(candidate.getTime() + 6e4);
|
|
8806
|
+
}
|
|
8807
|
+
return null;
|
|
8808
|
+
}
|
|
8809
|
+
matchesCron(parts, date) {
|
|
8810
|
+
const minute = date.getMinutes();
|
|
8811
|
+
const hour = date.getHours();
|
|
8812
|
+
const dayOfMonth = date.getDate();
|
|
8813
|
+
const month = date.getMonth() + 1;
|
|
8814
|
+
const dayOfWeek = date.getDay();
|
|
8815
|
+
return this.matchCronField(parts[0], minute) && this.matchCronField(parts[1], hour) && this.matchCronField(parts[2], dayOfMonth) && this.matchCronField(parts[3], month) && this.matchCronField(parts[4], dayOfWeek);
|
|
8816
|
+
}
|
|
8817
|
+
matchCronField(field, value) {
|
|
8818
|
+
if (field === "*")
|
|
8819
|
+
return true;
|
|
8820
|
+
const stepMatch = /^\*\/(\d+)$/.exec(field);
|
|
8821
|
+
if (stepMatch) {
|
|
8822
|
+
const step = parseInt(stepMatch[1], 10);
|
|
8823
|
+
return value % step === 0;
|
|
8824
|
+
}
|
|
8825
|
+
const num = parseInt(field, 10);
|
|
8826
|
+
if (!isNaN(num))
|
|
8827
|
+
return value === num;
|
|
8828
|
+
return false;
|
|
8829
|
+
}
|
|
8830
|
+
};
|
|
8831
|
+
}
|
|
8832
|
+
});
|
|
8833
|
+
|
|
6778
8834
|
// ../messaging/dist/adapter.js
|
|
6779
8835
|
import { EventEmitter } from "node:events";
|
|
6780
8836
|
var MessagingAdapter;
|
|
@@ -7749,8 +9805,8 @@ var init_signal = __esm({
|
|
|
7749
9805
|
});
|
|
7750
9806
|
|
|
7751
9807
|
// ../messaging/dist/index.js
|
|
7752
|
-
var
|
|
7753
|
-
__export(
|
|
9808
|
+
var dist_exports2 = {};
|
|
9809
|
+
__export(dist_exports2, {
|
|
7754
9810
|
DiscordAdapter: () => DiscordAdapter,
|
|
7755
9811
|
MatrixAdapter: () => MatrixAdapter,
|
|
7756
9812
|
MessagingAdapter: () => MessagingAdapter,
|
|
@@ -7771,8 +9827,8 @@ var init_dist7 = __esm({
|
|
|
7771
9827
|
});
|
|
7772
9828
|
|
|
7773
9829
|
// ../core/dist/alfred.js
|
|
7774
|
-
import
|
|
7775
|
-
import
|
|
9830
|
+
import fs7 from "node:fs";
|
|
9831
|
+
import path9 from "node:path";
|
|
7776
9832
|
import yaml2 from "js-yaml";
|
|
7777
9833
|
var Alfred;
|
|
7778
9834
|
var init_alfred = __esm({
|
|
@@ -7789,14 +9845,20 @@ var init_alfred = __esm({
|
|
|
7789
9845
|
init_speech_transcriber();
|
|
7790
9846
|
init_response_formatter();
|
|
7791
9847
|
init_embedding_service();
|
|
9848
|
+
init_document_processor();
|
|
9849
|
+
init_background_task_runner();
|
|
9850
|
+
init_proactive_scheduler();
|
|
7792
9851
|
Alfred = class {
|
|
7793
9852
|
config;
|
|
7794
9853
|
logger;
|
|
7795
9854
|
database;
|
|
7796
9855
|
pipeline;
|
|
7797
9856
|
reminderScheduler;
|
|
9857
|
+
backgroundTaskRunner;
|
|
9858
|
+
proactiveScheduler;
|
|
7798
9859
|
adapters = /* @__PURE__ */ new Map();
|
|
7799
9860
|
formatter = new ResponseFormatter();
|
|
9861
|
+
mcpManager;
|
|
7800
9862
|
calendarSkill;
|
|
7801
9863
|
// CalendarSkill instance for today's events
|
|
7802
9864
|
constructor(config) {
|
|
@@ -7814,6 +9876,9 @@ var init_alfred = __esm({
|
|
|
7814
9876
|
const reminderRepo = new ReminderRepository(db);
|
|
7815
9877
|
const noteRepo = new NoteRepository(db);
|
|
7816
9878
|
const embeddingRepo = new EmbeddingRepository(db);
|
|
9879
|
+
const linkTokenRepo = new LinkTokenRepository(db);
|
|
9880
|
+
const backgroundTaskRepo = new BackgroundTaskRepository(db);
|
|
9881
|
+
const scheduledActionRepo = new ScheduledActionRepository(db);
|
|
7817
9882
|
this.logger.info("Storage initialized");
|
|
7818
9883
|
const ruleEngine = new RuleEngine();
|
|
7819
9884
|
const rules = this.loadSecurityRules();
|
|
@@ -7850,6 +9915,12 @@ var init_alfred = __esm({
|
|
|
7850
9915
|
skillRegistry.register(new ScreenshotSkill());
|
|
7851
9916
|
skillRegistry.register(new BrowserSkill());
|
|
7852
9917
|
skillRegistry.register(new ProfileSkill(userRepo));
|
|
9918
|
+
skillRegistry.register(new CrossPlatformSkill(userRepo, linkTokenRepo, this.adapters));
|
|
9919
|
+
skillRegistry.register(new BackgroundTaskSkill(backgroundTaskRepo));
|
|
9920
|
+
skillRegistry.register(new ScheduledTaskSkill(scheduledActionRepo));
|
|
9921
|
+
const documentRepo = new DocumentRepository(db);
|
|
9922
|
+
const documentProcessor = new DocumentProcessor(documentRepo, embeddingService, this.logger.child({ component: "documents" }));
|
|
9923
|
+
skillRegistry.register(new DocumentSkill(documentRepo, documentProcessor, embeddingService));
|
|
7853
9924
|
let calendarSkill;
|
|
7854
9925
|
if (this.config.calendar) {
|
|
7855
9926
|
try {
|
|
@@ -7862,6 +9933,23 @@ var init_alfred = __esm({
|
|
|
7862
9933
|
}
|
|
7863
9934
|
}
|
|
7864
9935
|
this.calendarSkill = calendarSkill;
|
|
9936
|
+
if (this.config.mcp?.servers?.length) {
|
|
9937
|
+
const { MCPManager: MCPManager2 } = await Promise.resolve().then(() => (init_dist6(), dist_exports));
|
|
9938
|
+
this.mcpManager = new MCPManager2(this.logger.child({ component: "mcp" }));
|
|
9939
|
+
await this.mcpManager.initialize(this.config.mcp);
|
|
9940
|
+
for (const skill of this.mcpManager.getSkills()) {
|
|
9941
|
+
skillRegistry.register(skill);
|
|
9942
|
+
}
|
|
9943
|
+
this.logger.info({ mcpSkills: this.mcpManager.getSkills().length }, "MCP skills registered");
|
|
9944
|
+
}
|
|
9945
|
+
if (this.config.codeSandbox?.enabled) {
|
|
9946
|
+
const { CodeExecutionSkill: CodeExecutionSkill2 } = await Promise.resolve().then(() => (init_dist6(), dist_exports));
|
|
9947
|
+
skillRegistry.register(new CodeExecutionSkill2({
|
|
9948
|
+
allowedLanguages: this.config.codeSandbox.allowedLanguages,
|
|
9949
|
+
maxTimeoutMs: this.config.codeSandbox.maxTimeoutMs
|
|
9950
|
+
}));
|
|
9951
|
+
this.logger.info("Code sandbox enabled");
|
|
9952
|
+
}
|
|
7865
9953
|
this.logger.info({ skills: skillRegistry.getAll().map((s) => s.metadata.name) }, "Skills registered");
|
|
7866
9954
|
let speechTranscriber;
|
|
7867
9955
|
if (this.config.speech?.apiKey) {
|
|
@@ -7869,7 +9957,7 @@ var init_alfred = __esm({
|
|
|
7869
9957
|
this.logger.info({ provider: this.config.speech.provider }, "Speech-to-text initialized");
|
|
7870
9958
|
}
|
|
7871
9959
|
const conversationManager = new ConversationManager(conversationRepo);
|
|
7872
|
-
const inboxPath =
|
|
9960
|
+
const inboxPath = path9.resolve(path9.dirname(this.config.storage.path), "inbox");
|
|
7873
9961
|
this.pipeline = new MessagePipeline({
|
|
7874
9962
|
llm: llmProvider,
|
|
7875
9963
|
conversationManager,
|
|
@@ -7891,33 +9979,35 @@ var init_alfred = __esm({
|
|
|
7891
9979
|
this.logger.warn({ platform, chatId }, "No adapter for reminder platform");
|
|
7892
9980
|
}
|
|
7893
9981
|
}, this.logger.child({ component: "reminders" }));
|
|
9982
|
+
this.backgroundTaskRunner = new BackgroundTaskRunner(skillRegistry, skillSandbox, backgroundTaskRepo, this.adapters, this.logger.child({ component: "background-tasks" }));
|
|
9983
|
+
this.proactiveScheduler = new ProactiveScheduler(scheduledActionRepo, skillRegistry, skillSandbox, llmProvider, this.adapters, this.logger.child({ component: "proactive-scheduler" }));
|
|
7894
9984
|
await this.initializeAdapters();
|
|
7895
9985
|
this.logger.info("Alfred initialized");
|
|
7896
9986
|
}
|
|
7897
9987
|
async initializeAdapters() {
|
|
7898
9988
|
const { config } = this;
|
|
7899
9989
|
if (config.telegram.enabled && config.telegram.token) {
|
|
7900
|
-
const { TelegramAdapter: TelegramAdapter2 } = await Promise.resolve().then(() => (init_dist7(),
|
|
9990
|
+
const { TelegramAdapter: TelegramAdapter2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
|
|
7901
9991
|
this.adapters.set("telegram", new TelegramAdapter2(config.telegram.token));
|
|
7902
9992
|
this.logger.info("Telegram adapter registered");
|
|
7903
9993
|
}
|
|
7904
9994
|
if (config.discord?.enabled && config.discord.token) {
|
|
7905
|
-
const { DiscordAdapter: DiscordAdapter2 } = await Promise.resolve().then(() => (init_dist7(),
|
|
9995
|
+
const { DiscordAdapter: DiscordAdapter2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
|
|
7906
9996
|
this.adapters.set("discord", new DiscordAdapter2(config.discord.token));
|
|
7907
9997
|
this.logger.info("Discord adapter registered");
|
|
7908
9998
|
}
|
|
7909
9999
|
if (config.whatsapp?.enabled) {
|
|
7910
|
-
const { WhatsAppAdapter: WhatsAppAdapter2 } = await Promise.resolve().then(() => (init_dist7(),
|
|
10000
|
+
const { WhatsAppAdapter: WhatsAppAdapter2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
|
|
7911
10001
|
this.adapters.set("whatsapp", new WhatsAppAdapter2(config.whatsapp.dataPath));
|
|
7912
10002
|
this.logger.info("WhatsApp adapter registered");
|
|
7913
10003
|
}
|
|
7914
10004
|
if (config.matrix?.enabled && config.matrix.accessToken) {
|
|
7915
|
-
const { MatrixAdapter: MatrixAdapter2 } = await Promise.resolve().then(() => (init_dist7(),
|
|
10005
|
+
const { MatrixAdapter: MatrixAdapter2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
|
|
7916
10006
|
this.adapters.set("matrix", new MatrixAdapter2(config.matrix.homeserverUrl, config.matrix.accessToken, config.matrix.userId));
|
|
7917
10007
|
this.logger.info("Matrix adapter registered");
|
|
7918
10008
|
}
|
|
7919
10009
|
if (config.signal?.enabled && config.signal.phoneNumber) {
|
|
7920
|
-
const { SignalAdapter: SignalAdapter2 } = await Promise.resolve().then(() => (init_dist7(),
|
|
10010
|
+
const { SignalAdapter: SignalAdapter2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
|
|
7921
10011
|
this.adapters.set("signal", new SignalAdapter2(config.signal.apiUrl, config.signal.phoneNumber));
|
|
7922
10012
|
this.logger.info("Signal adapter registered");
|
|
7923
10013
|
}
|
|
@@ -7930,6 +10020,8 @@ var init_alfred = __esm({
|
|
|
7930
10020
|
this.logger.info({ platform }, "Adapter connected");
|
|
7931
10021
|
}
|
|
7932
10022
|
this.reminderScheduler?.start();
|
|
10023
|
+
this.backgroundTaskRunner?.start();
|
|
10024
|
+
this.proactiveScheduler?.start();
|
|
7933
10025
|
if (this.adapters.size === 0) {
|
|
7934
10026
|
this.logger.warn("No messaging adapters enabled. Configure at least one platform.");
|
|
7935
10027
|
}
|
|
@@ -7938,6 +10030,11 @@ var init_alfred = __esm({
|
|
|
7938
10030
|
async stop() {
|
|
7939
10031
|
this.logger.info("Stopping Alfred...");
|
|
7940
10032
|
this.reminderScheduler?.stop();
|
|
10033
|
+
this.backgroundTaskRunner?.stop();
|
|
10034
|
+
this.proactiveScheduler?.stop();
|
|
10035
|
+
if (this.mcpManager) {
|
|
10036
|
+
await this.mcpManager.shutdown();
|
|
10037
|
+
}
|
|
7941
10038
|
for (const [platform, adapter] of this.adapters) {
|
|
7942
10039
|
try {
|
|
7943
10040
|
await adapter.disconnect();
|
|
@@ -7999,22 +10096,22 @@ var init_alfred = __esm({
|
|
|
7999
10096
|
});
|
|
8000
10097
|
}
|
|
8001
10098
|
loadSecurityRules() {
|
|
8002
|
-
const rulesPath =
|
|
10099
|
+
const rulesPath = path9.resolve(this.config.security.rulesPath);
|
|
8003
10100
|
const rules = [];
|
|
8004
|
-
if (!
|
|
10101
|
+
if (!fs7.existsSync(rulesPath)) {
|
|
8005
10102
|
this.logger.warn({ rulesPath }, "Security rules directory not found, using default deny");
|
|
8006
10103
|
return rules;
|
|
8007
10104
|
}
|
|
8008
|
-
const stat =
|
|
10105
|
+
const stat = fs7.statSync(rulesPath);
|
|
8009
10106
|
if (!stat.isDirectory()) {
|
|
8010
10107
|
this.logger.warn({ rulesPath }, "Security rules path is not a directory");
|
|
8011
10108
|
return rules;
|
|
8012
10109
|
}
|
|
8013
|
-
const files =
|
|
10110
|
+
const files = fs7.readdirSync(rulesPath).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
8014
10111
|
for (const file of files) {
|
|
8015
10112
|
try {
|
|
8016
|
-
const filePath =
|
|
8017
|
-
const content =
|
|
10113
|
+
const filePath = path9.join(rulesPath, file);
|
|
10114
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
8018
10115
|
const parsed = yaml2.load(content);
|
|
8019
10116
|
if (parsed?.rules && Array.isArray(parsed.rules)) {
|
|
8020
10117
|
rules.push(...parsed.rules);
|
|
@@ -8041,6 +10138,9 @@ var init_dist8 = __esm({
|
|
|
8041
10138
|
init_speech_transcriber();
|
|
8042
10139
|
init_response_formatter();
|
|
8043
10140
|
init_embedding_service();
|
|
10141
|
+
init_background_task_runner();
|
|
10142
|
+
init_proactive_scheduler();
|
|
10143
|
+
init_document_processor();
|
|
8044
10144
|
}
|
|
8045
10145
|
});
|
|
8046
10146
|
|
|
@@ -8111,8 +10211,8 @@ __export(setup_exports, {
|
|
|
8111
10211
|
});
|
|
8112
10212
|
import { createInterface } from "node:readline/promises";
|
|
8113
10213
|
import { stdin as input, stdout as output } from "node:process";
|
|
8114
|
-
import
|
|
8115
|
-
import
|
|
10214
|
+
import fs8 from "node:fs";
|
|
10215
|
+
import path10 from "node:path";
|
|
8116
10216
|
import yaml3 from "js-yaml";
|
|
8117
10217
|
function green(s) {
|
|
8118
10218
|
return `${GREEN}${s}${RESET}`;
|
|
@@ -8143,20 +10243,20 @@ function loadExistingConfig(projectRoot) {
|
|
|
8143
10243
|
let shellEnabled = false;
|
|
8144
10244
|
let writeInGroups = false;
|
|
8145
10245
|
let rateLimit = 30;
|
|
8146
|
-
const configPath =
|
|
8147
|
-
if (
|
|
10246
|
+
const configPath = path10.join(projectRoot, "config", "default.yml");
|
|
10247
|
+
if (fs8.existsSync(configPath)) {
|
|
8148
10248
|
try {
|
|
8149
|
-
const parsed = yaml3.load(
|
|
10249
|
+
const parsed = yaml3.load(fs8.readFileSync(configPath, "utf-8"));
|
|
8150
10250
|
if (parsed && typeof parsed === "object") {
|
|
8151
10251
|
Object.assign(config, parsed);
|
|
8152
10252
|
}
|
|
8153
10253
|
} catch {
|
|
8154
10254
|
}
|
|
8155
10255
|
}
|
|
8156
|
-
const envPath =
|
|
8157
|
-
if (
|
|
10256
|
+
const envPath = path10.join(projectRoot, ".env");
|
|
10257
|
+
if (fs8.existsSync(envPath)) {
|
|
8158
10258
|
try {
|
|
8159
|
-
const lines =
|
|
10259
|
+
const lines = fs8.readFileSync(envPath, "utf-8").split("\n");
|
|
8160
10260
|
for (const line of lines) {
|
|
8161
10261
|
const trimmed = line.trim();
|
|
8162
10262
|
if (!trimmed || trimmed.startsWith("#"))
|
|
@@ -8169,10 +10269,10 @@ function loadExistingConfig(projectRoot) {
|
|
|
8169
10269
|
} catch {
|
|
8170
10270
|
}
|
|
8171
10271
|
}
|
|
8172
|
-
const rulesPath =
|
|
8173
|
-
if (
|
|
10272
|
+
const rulesPath = path10.join(projectRoot, "config", "rules", "default-rules.yml");
|
|
10273
|
+
if (fs8.existsSync(rulesPath)) {
|
|
8174
10274
|
try {
|
|
8175
|
-
const rulesContent = yaml3.load(
|
|
10275
|
+
const rulesContent = yaml3.load(fs8.readFileSync(rulesPath, "utf-8"));
|
|
8176
10276
|
if (rulesContent?.rules) {
|
|
8177
10277
|
shellEnabled = rulesContent.rules.some((r) => r.id === "allow-owner-admin" && r.effect === "allow");
|
|
8178
10278
|
const writeDmRule = rulesContent.rules.find((r) => r.id === "allow-write-for-dm" || r.id === "allow-write-all");
|
|
@@ -8589,12 +10689,12 @@ ${bold("Writing configuration files...")}`);
|
|
|
8589
10689
|
envLines.push("# ALFRED_OWNER_USER_ID=");
|
|
8590
10690
|
}
|
|
8591
10691
|
envLines.push("");
|
|
8592
|
-
const envPath =
|
|
8593
|
-
|
|
10692
|
+
const envPath = path10.join(projectRoot, ".env");
|
|
10693
|
+
fs8.writeFileSync(envPath, envLines.join("\n"), "utf-8");
|
|
8594
10694
|
console.log(` ${green("+")} ${dim(".env")} written`);
|
|
8595
|
-
const configDir =
|
|
8596
|
-
if (!
|
|
8597
|
-
|
|
10695
|
+
const configDir = path10.join(projectRoot, "config");
|
|
10696
|
+
if (!fs8.existsSync(configDir)) {
|
|
10697
|
+
fs8.mkdirSync(configDir, { recursive: true });
|
|
8598
10698
|
}
|
|
8599
10699
|
const config = {
|
|
8600
10700
|
name: botName,
|
|
@@ -8666,12 +10766,12 @@ ${bold("Writing configuration files...")}`);
|
|
|
8666
10766
|
config.security.ownerUserId = ownerUserId;
|
|
8667
10767
|
}
|
|
8668
10768
|
const yamlStr = "# Alfred \u2014 Configuration\n# Generated by `alfred setup`\n# Edit manually or re-run `alfred setup` to reconfigure.\n\n" + yaml3.dump(config, { lineWidth: 120, noRefs: true, sortKeys: false });
|
|
8669
|
-
const configPath =
|
|
8670
|
-
|
|
10769
|
+
const configPath = path10.join(configDir, "default.yml");
|
|
10770
|
+
fs8.writeFileSync(configPath, yamlStr, "utf-8");
|
|
8671
10771
|
console.log(` ${green("+")} ${dim("config/default.yml")} written`);
|
|
8672
|
-
const rulesDir =
|
|
8673
|
-
if (!
|
|
8674
|
-
|
|
10772
|
+
const rulesDir = path10.join(configDir, "rules");
|
|
10773
|
+
if (!fs8.existsSync(rulesDir)) {
|
|
10774
|
+
fs8.mkdirSync(rulesDir, { recursive: true });
|
|
8675
10775
|
}
|
|
8676
10776
|
const ownerAdminRule = enableShell && ownerUserId ? `
|
|
8677
10777
|
# Allow admin actions (shell, etc.) for the owner only
|
|
@@ -8752,12 +10852,12 @@ ${ownerAdminRule}
|
|
|
8752
10852
|
actions: ["*"]
|
|
8753
10853
|
riskLevels: [read, write, destructive, admin]
|
|
8754
10854
|
`;
|
|
8755
|
-
const rulesPath =
|
|
8756
|
-
|
|
10855
|
+
const rulesPath = path10.join(rulesDir, "default-rules.yml");
|
|
10856
|
+
fs8.writeFileSync(rulesPath, rulesYaml, "utf-8");
|
|
8757
10857
|
console.log(` ${green("+")} ${dim("config/rules/default-rules.yml")} written`);
|
|
8758
|
-
const dataDir =
|
|
8759
|
-
if (!
|
|
8760
|
-
|
|
10858
|
+
const dataDir = path10.join(projectRoot, "data");
|
|
10859
|
+
if (!fs8.existsSync(dataDir)) {
|
|
10860
|
+
fs8.mkdirSync(dataDir, { recursive: true });
|
|
8761
10861
|
console.log(` ${green("+")} ${dim("data/")} directory created`);
|
|
8762
10862
|
}
|
|
8763
10863
|
console.log("");
|
|
@@ -9039,8 +11139,8 @@ var rules_exports = {};
|
|
|
9039
11139
|
__export(rules_exports, {
|
|
9040
11140
|
rulesCommand: () => rulesCommand
|
|
9041
11141
|
});
|
|
9042
|
-
import
|
|
9043
|
-
import
|
|
11142
|
+
import fs9 from "node:fs";
|
|
11143
|
+
import path11 from "node:path";
|
|
9044
11144
|
import yaml4 from "js-yaml";
|
|
9045
11145
|
async function rulesCommand() {
|
|
9046
11146
|
const configLoader = new ConfigLoader();
|
|
@@ -9051,18 +11151,18 @@ async function rulesCommand() {
|
|
|
9051
11151
|
console.error("Failed to load configuration:", error.message);
|
|
9052
11152
|
process.exit(1);
|
|
9053
11153
|
}
|
|
9054
|
-
const rulesPath =
|
|
9055
|
-
if (!
|
|
11154
|
+
const rulesPath = path11.resolve(config.security.rulesPath);
|
|
11155
|
+
if (!fs9.existsSync(rulesPath)) {
|
|
9056
11156
|
console.log(`Rules directory not found: ${rulesPath}`);
|
|
9057
11157
|
console.log("No security rules loaded.");
|
|
9058
11158
|
return;
|
|
9059
11159
|
}
|
|
9060
|
-
const stat =
|
|
11160
|
+
const stat = fs9.statSync(rulesPath);
|
|
9061
11161
|
if (!stat.isDirectory()) {
|
|
9062
11162
|
console.error(`Rules path is not a directory: ${rulesPath}`);
|
|
9063
11163
|
process.exit(1);
|
|
9064
11164
|
}
|
|
9065
|
-
const files =
|
|
11165
|
+
const files = fs9.readdirSync(rulesPath).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
9066
11166
|
if (files.length === 0) {
|
|
9067
11167
|
console.log(`No YAML rule files found in: ${rulesPath}`);
|
|
9068
11168
|
return;
|
|
@@ -9071,9 +11171,9 @@ async function rulesCommand() {
|
|
|
9071
11171
|
const allRules = [];
|
|
9072
11172
|
const errors = [];
|
|
9073
11173
|
for (const file of files) {
|
|
9074
|
-
const filePath =
|
|
11174
|
+
const filePath = path11.join(rulesPath, file);
|
|
9075
11175
|
try {
|
|
9076
|
-
const raw =
|
|
11176
|
+
const raw = fs9.readFileSync(filePath, "utf-8");
|
|
9077
11177
|
const parsed = yaml4.load(raw);
|
|
9078
11178
|
const rules = ruleLoader.loadFromObject(parsed);
|
|
9079
11179
|
allRules.push(...rules);
|
|
@@ -9125,8 +11225,8 @@ var status_exports = {};
|
|
|
9125
11225
|
__export(status_exports, {
|
|
9126
11226
|
statusCommand: () => statusCommand
|
|
9127
11227
|
});
|
|
9128
|
-
import
|
|
9129
|
-
import
|
|
11228
|
+
import fs10 from "node:fs";
|
|
11229
|
+
import path12 from "node:path";
|
|
9130
11230
|
import yaml5 from "js-yaml";
|
|
9131
11231
|
async function statusCommand() {
|
|
9132
11232
|
const configLoader = new ConfigLoader();
|
|
@@ -9183,22 +11283,22 @@ async function statusCommand() {
|
|
|
9183
11283
|
}
|
|
9184
11284
|
console.log("");
|
|
9185
11285
|
console.log("Storage:");
|
|
9186
|
-
const dbPath =
|
|
9187
|
-
const dbExists =
|
|
11286
|
+
const dbPath = path12.resolve(config.storage.path);
|
|
11287
|
+
const dbExists = fs10.existsSync(dbPath);
|
|
9188
11288
|
console.log(` Database: ${dbPath}`);
|
|
9189
11289
|
console.log(` Status: ${dbExists ? "exists" : "not yet created"}`);
|
|
9190
11290
|
console.log("");
|
|
9191
|
-
const rulesPath =
|
|
11291
|
+
const rulesPath = path12.resolve(config.security.rulesPath);
|
|
9192
11292
|
let ruleCount = 0;
|
|
9193
11293
|
let ruleFileCount = 0;
|
|
9194
|
-
if (
|
|
9195
|
-
const files =
|
|
11294
|
+
if (fs10.existsSync(rulesPath) && fs10.statSync(rulesPath).isDirectory()) {
|
|
11295
|
+
const files = fs10.readdirSync(rulesPath).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
9196
11296
|
ruleFileCount = files.length;
|
|
9197
11297
|
const ruleLoader = new RuleLoader();
|
|
9198
11298
|
for (const file of files) {
|
|
9199
|
-
const filePath =
|
|
11299
|
+
const filePath = path12.join(rulesPath, file);
|
|
9200
11300
|
try {
|
|
9201
|
-
const raw =
|
|
11301
|
+
const raw = fs10.readFileSync(filePath, "utf-8");
|
|
9202
11302
|
const parsed = yaml5.load(raw);
|
|
9203
11303
|
const rules = ruleLoader.loadFromObject(parsed);
|
|
9204
11304
|
ruleCount += rules.length;
|
|
@@ -9232,8 +11332,8 @@ var logs_exports = {};
|
|
|
9232
11332
|
__export(logs_exports, {
|
|
9233
11333
|
logsCommand: () => logsCommand
|
|
9234
11334
|
});
|
|
9235
|
-
import
|
|
9236
|
-
import
|
|
11335
|
+
import fs11 from "node:fs";
|
|
11336
|
+
import path13 from "node:path";
|
|
9237
11337
|
async function logsCommand(tail) {
|
|
9238
11338
|
const configLoader = new ConfigLoader();
|
|
9239
11339
|
let config;
|
|
@@ -9243,8 +11343,8 @@ async function logsCommand(tail) {
|
|
|
9243
11343
|
console.error("Failed to load configuration:", error.message);
|
|
9244
11344
|
process.exit(1);
|
|
9245
11345
|
}
|
|
9246
|
-
const dbPath =
|
|
9247
|
-
if (!
|
|
11346
|
+
const dbPath = path13.resolve(config.storage.path);
|
|
11347
|
+
if (!fs11.existsSync(dbPath)) {
|
|
9248
11348
|
console.log(`Database not found at: ${dbPath}`);
|
|
9249
11349
|
console.log("No audit log entries. Alfred has not been run yet, or the database path is incorrect.");
|
|
9250
11350
|
return;
|