@madh-io/alfred-ai 0.1.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 +4292 -0
- package/package.json +63 -0
package/bundle/index.js
ADDED
|
@@ -0,0 +1,4292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// ../config/dist/schema.js
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
var TelegramConfigSchema, DiscordConfigSchema, WhatsAppConfigSchema, MatrixConfigSchema, SignalConfigSchema, StorageConfigSchema, LoggerConfigSchema, SecurityConfigSchema, LLMProviderConfigSchema, AlfredConfigSchema;
|
|
15
|
+
var init_schema = __esm({
|
|
16
|
+
"../config/dist/schema.js"() {
|
|
17
|
+
"use strict";
|
|
18
|
+
TelegramConfigSchema = z.object({
|
|
19
|
+
token: z.string().default(""),
|
|
20
|
+
enabled: z.boolean()
|
|
21
|
+
});
|
|
22
|
+
DiscordConfigSchema = z.object({
|
|
23
|
+
token: z.string().default(""),
|
|
24
|
+
enabled: z.boolean()
|
|
25
|
+
});
|
|
26
|
+
WhatsAppConfigSchema = z.object({
|
|
27
|
+
enabled: z.boolean(),
|
|
28
|
+
dataPath: z.string()
|
|
29
|
+
});
|
|
30
|
+
MatrixConfigSchema = z.object({
|
|
31
|
+
homeserverUrl: z.string(),
|
|
32
|
+
accessToken: z.string().default(""),
|
|
33
|
+
userId: z.string().default(""),
|
|
34
|
+
enabled: z.boolean()
|
|
35
|
+
});
|
|
36
|
+
SignalConfigSchema = z.object({
|
|
37
|
+
apiUrl: z.string(),
|
|
38
|
+
phoneNumber: z.string().default(""),
|
|
39
|
+
enabled: z.boolean()
|
|
40
|
+
});
|
|
41
|
+
StorageConfigSchema = z.object({
|
|
42
|
+
path: z.string()
|
|
43
|
+
});
|
|
44
|
+
LoggerConfigSchema = z.object({
|
|
45
|
+
level: z.enum(["trace", "debug", "info", "warn", "error", "fatal"]),
|
|
46
|
+
pretty: z.boolean(),
|
|
47
|
+
auditLogPath: z.string().optional()
|
|
48
|
+
});
|
|
49
|
+
SecurityConfigSchema = z.object({
|
|
50
|
+
rulesPath: z.string(),
|
|
51
|
+
defaultEffect: z.enum(["allow", "deny"]),
|
|
52
|
+
ownerUserId: z.string().optional()
|
|
53
|
+
});
|
|
54
|
+
LLMProviderConfigSchema = z.object({
|
|
55
|
+
provider: z.enum(["anthropic", "openai", "openrouter", "ollama"]),
|
|
56
|
+
apiKey: z.string().default(""),
|
|
57
|
+
baseUrl: z.string().optional(),
|
|
58
|
+
model: z.string(),
|
|
59
|
+
temperature: z.number().optional(),
|
|
60
|
+
maxTokens: z.number().optional()
|
|
61
|
+
});
|
|
62
|
+
AlfredConfigSchema = z.object({
|
|
63
|
+
name: z.string(),
|
|
64
|
+
telegram: TelegramConfigSchema,
|
|
65
|
+
discord: DiscordConfigSchema.optional(),
|
|
66
|
+
whatsapp: WhatsAppConfigSchema.optional(),
|
|
67
|
+
matrix: MatrixConfigSchema.optional(),
|
|
68
|
+
signal: SignalConfigSchema.optional(),
|
|
69
|
+
llm: LLMProviderConfigSchema,
|
|
70
|
+
storage: StorageConfigSchema,
|
|
71
|
+
logger: LoggerConfigSchema,
|
|
72
|
+
security: SecurityConfigSchema
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// ../config/dist/defaults.js
|
|
78
|
+
var DEFAULT_CONFIG;
|
|
79
|
+
var init_defaults = __esm({
|
|
80
|
+
"../config/dist/defaults.js"() {
|
|
81
|
+
"use strict";
|
|
82
|
+
DEFAULT_CONFIG = {
|
|
83
|
+
name: "Alfred",
|
|
84
|
+
telegram: {
|
|
85
|
+
token: "",
|
|
86
|
+
enabled: false
|
|
87
|
+
},
|
|
88
|
+
discord: {
|
|
89
|
+
token: "",
|
|
90
|
+
enabled: false
|
|
91
|
+
},
|
|
92
|
+
whatsapp: {
|
|
93
|
+
enabled: false,
|
|
94
|
+
dataPath: "./data/whatsapp"
|
|
95
|
+
},
|
|
96
|
+
matrix: {
|
|
97
|
+
homeserverUrl: "https://matrix.org",
|
|
98
|
+
accessToken: "",
|
|
99
|
+
userId: "",
|
|
100
|
+
enabled: false
|
|
101
|
+
},
|
|
102
|
+
signal: {
|
|
103
|
+
apiUrl: "http://localhost:8080",
|
|
104
|
+
phoneNumber: "",
|
|
105
|
+
enabled: false
|
|
106
|
+
},
|
|
107
|
+
llm: {
|
|
108
|
+
provider: "anthropic",
|
|
109
|
+
model: "claude-sonnet-4-20250514",
|
|
110
|
+
temperature: 0.7,
|
|
111
|
+
maxTokens: 4096
|
|
112
|
+
},
|
|
113
|
+
storage: {
|
|
114
|
+
path: "./data/alfred.db"
|
|
115
|
+
},
|
|
116
|
+
logger: {
|
|
117
|
+
level: "info",
|
|
118
|
+
pretty: true
|
|
119
|
+
},
|
|
120
|
+
security: {
|
|
121
|
+
rulesPath: "./config/rules",
|
|
122
|
+
defaultEffect: "deny"
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// ../config/dist/loader.js
|
|
129
|
+
import fs from "node:fs";
|
|
130
|
+
import path from "node:path";
|
|
131
|
+
import { config as loadDotenv } from "dotenv";
|
|
132
|
+
import yaml from "js-yaml";
|
|
133
|
+
function deepMerge(target, source) {
|
|
134
|
+
const result = { ...target };
|
|
135
|
+
for (const key of Object.keys(source)) {
|
|
136
|
+
const sourceVal = source[key];
|
|
137
|
+
const targetVal = result[key];
|
|
138
|
+
if (sourceVal !== null && sourceVal !== void 0 && typeof sourceVal === "object" && !Array.isArray(sourceVal) && targetVal !== null && targetVal !== void 0 && typeof targetVal === "object" && !Array.isArray(targetVal)) {
|
|
139
|
+
result[key] = deepMerge(targetVal, sourceVal);
|
|
140
|
+
} else {
|
|
141
|
+
result[key] = sourceVal;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
function applyEnvOverrides(config) {
|
|
147
|
+
const result = { ...config };
|
|
148
|
+
for (const [envVar, keyPath] of Object.entries(ENV_MAP)) {
|
|
149
|
+
const value = process.env[envVar];
|
|
150
|
+
if (value === void 0)
|
|
151
|
+
continue;
|
|
152
|
+
let current = result;
|
|
153
|
+
for (let i = 0; i < keyPath.length - 1; i++) {
|
|
154
|
+
const key = keyPath[i];
|
|
155
|
+
if (current[key] === void 0 || current[key] === null || typeof current[key] !== "object") {
|
|
156
|
+
current[key] = {};
|
|
157
|
+
}
|
|
158
|
+
current[key] = { ...current[key] };
|
|
159
|
+
current = current[key];
|
|
160
|
+
}
|
|
161
|
+
current[keyPath[keyPath.length - 1]] = value;
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
var ENV_MAP, ConfigLoader;
|
|
166
|
+
var init_loader = __esm({
|
|
167
|
+
"../config/dist/loader.js"() {
|
|
168
|
+
"use strict";
|
|
169
|
+
init_schema();
|
|
170
|
+
init_defaults();
|
|
171
|
+
ENV_MAP = {
|
|
172
|
+
ALFRED_TELEGRAM_TOKEN: ["telegram", "token"],
|
|
173
|
+
ALFRED_DISCORD_TOKEN: ["discord", "token"],
|
|
174
|
+
ALFRED_MATRIX_HOMESERVER_URL: ["matrix", "homeserverUrl"],
|
|
175
|
+
ALFRED_MATRIX_ACCESS_TOKEN: ["matrix", "accessToken"],
|
|
176
|
+
ALFRED_MATRIX_USER_ID: ["matrix", "userId"],
|
|
177
|
+
ALFRED_SIGNAL_API_URL: ["signal", "apiUrl"],
|
|
178
|
+
ALFRED_SIGNAL_PHONE_NUMBER: ["signal", "phoneNumber"],
|
|
179
|
+
ALFRED_ANTHROPIC_API_KEY: ["llm", "apiKey"],
|
|
180
|
+
ALFRED_OPENAI_API_KEY: ["llm", "apiKey"],
|
|
181
|
+
ALFRED_OPENROUTER_API_KEY: ["llm", "apiKey"],
|
|
182
|
+
ALFRED_LLM_PROVIDER: ["llm", "provider"],
|
|
183
|
+
ALFRED_LLM_MODEL: ["llm", "model"],
|
|
184
|
+
ALFRED_LLM_BASE_URL: ["llm", "baseUrl"],
|
|
185
|
+
ALFRED_STORAGE_PATH: ["storage", "path"],
|
|
186
|
+
ALFRED_LOG_LEVEL: ["logger", "level"],
|
|
187
|
+
ALFRED_OWNER_USER_ID: ["security", "ownerUserId"]
|
|
188
|
+
};
|
|
189
|
+
ConfigLoader = class {
|
|
190
|
+
loadConfig(configPath) {
|
|
191
|
+
loadDotenv();
|
|
192
|
+
const resolvedPath = configPath ?? process.env["ALFRED_CONFIG_PATH"] ?? "./config/default.yml";
|
|
193
|
+
let fileConfig = {};
|
|
194
|
+
const absolutePath = path.resolve(resolvedPath);
|
|
195
|
+
if (fs.existsSync(absolutePath)) {
|
|
196
|
+
const raw = fs.readFileSync(absolutePath, "utf-8");
|
|
197
|
+
const parsed = yaml.load(raw);
|
|
198
|
+
if (parsed && typeof parsed === "object") {
|
|
199
|
+
fileConfig = parsed;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const merged = deepMerge(DEFAULT_CONFIG, fileConfig);
|
|
203
|
+
const withEnv = applyEnvOverrides(merged);
|
|
204
|
+
const validated = AlfredConfigSchema.parse(withEnv);
|
|
205
|
+
return validated;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// ../config/dist/index.js
|
|
212
|
+
var init_dist = __esm({
|
|
213
|
+
"../config/dist/index.js"() {
|
|
214
|
+
"use strict";
|
|
215
|
+
init_schema();
|
|
216
|
+
init_defaults();
|
|
217
|
+
init_loader();
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// ../logger/dist/logger.js
|
|
222
|
+
import pino from "pino";
|
|
223
|
+
function createLogger(name, level) {
|
|
224
|
+
const logLevel = level ?? process.env.LOG_LEVEL ?? "info";
|
|
225
|
+
const usePretty = logLevel === "debug" || logLevel === "trace" || process.env.NODE_ENV !== "production";
|
|
226
|
+
if (usePretty) {
|
|
227
|
+
const transport = pino.transport({
|
|
228
|
+
target: "pino-pretty",
|
|
229
|
+
options: { colorize: true }
|
|
230
|
+
});
|
|
231
|
+
return pino({ name, level: logLevel }, transport);
|
|
232
|
+
}
|
|
233
|
+
return pino({ name, level: logLevel });
|
|
234
|
+
}
|
|
235
|
+
var init_logger = __esm({
|
|
236
|
+
"../logger/dist/logger.js"() {
|
|
237
|
+
"use strict";
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ../logger/dist/audit.js
|
|
242
|
+
import pino2 from "pino";
|
|
243
|
+
var init_audit = __esm({
|
|
244
|
+
"../logger/dist/audit.js"() {
|
|
245
|
+
"use strict";
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// ../logger/dist/index.js
|
|
250
|
+
var init_dist2 = __esm({
|
|
251
|
+
"../logger/dist/index.js"() {
|
|
252
|
+
"use strict";
|
|
253
|
+
init_logger();
|
|
254
|
+
init_audit();
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// ../storage/dist/database.js
|
|
259
|
+
import BetterSqlite3 from "better-sqlite3";
|
|
260
|
+
import fs2 from "node:fs";
|
|
261
|
+
import path2 from "node:path";
|
|
262
|
+
var Database;
|
|
263
|
+
var init_database = __esm({
|
|
264
|
+
"../storage/dist/database.js"() {
|
|
265
|
+
"use strict";
|
|
266
|
+
Database = class {
|
|
267
|
+
db;
|
|
268
|
+
constructor(dbPath) {
|
|
269
|
+
const dir = path2.dirname(dbPath);
|
|
270
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
271
|
+
this.db = new BetterSqlite3(dbPath);
|
|
272
|
+
this.db.pragma("journal_mode = WAL");
|
|
273
|
+
this.initTables();
|
|
274
|
+
}
|
|
275
|
+
initTables() {
|
|
276
|
+
this.db.exec(`
|
|
277
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
278
|
+
id TEXT PRIMARY KEY,
|
|
279
|
+
platform TEXT NOT NULL,
|
|
280
|
+
chat_id TEXT NOT NULL,
|
|
281
|
+
user_id TEXT NOT NULL,
|
|
282
|
+
created_at TEXT NOT NULL,
|
|
283
|
+
updated_at TEXT NOT NULL
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
287
|
+
id TEXT PRIMARY KEY,
|
|
288
|
+
conversation_id TEXT NOT NULL REFERENCES conversations(id),
|
|
289
|
+
role TEXT NOT NULL,
|
|
290
|
+
content TEXT NOT NULL,
|
|
291
|
+
tool_calls TEXT,
|
|
292
|
+
created_at TEXT NOT NULL
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
296
|
+
id TEXT PRIMARY KEY,
|
|
297
|
+
platform TEXT NOT NULL,
|
|
298
|
+
platform_user_id TEXT NOT NULL,
|
|
299
|
+
username TEXT,
|
|
300
|
+
display_name TEXT,
|
|
301
|
+
created_at TEXT NOT NULL,
|
|
302
|
+
updated_at TEXT NOT NULL
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
CREATE TABLE IF NOT EXISTS audit_log (
|
|
306
|
+
id TEXT PRIMARY KEY,
|
|
307
|
+
timestamp TEXT NOT NULL,
|
|
308
|
+
user_id TEXT NOT NULL,
|
|
309
|
+
action TEXT NOT NULL,
|
|
310
|
+
risk_level TEXT NOT NULL,
|
|
311
|
+
rule_id TEXT,
|
|
312
|
+
effect TEXT NOT NULL,
|
|
313
|
+
platform TEXT NOT NULL,
|
|
314
|
+
chat_id TEXT,
|
|
315
|
+
context TEXT
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_platform_chat
|
|
319
|
+
ON conversations(platform, chat_id);
|
|
320
|
+
|
|
321
|
+
CREATE INDEX IF NOT EXISTS idx_messages_conversation
|
|
322
|
+
ON messages(conversation_id, created_at);
|
|
323
|
+
|
|
324
|
+
CREATE INDEX IF NOT EXISTS idx_users_platform
|
|
325
|
+
ON users(platform, platform_user_id);
|
|
326
|
+
`);
|
|
327
|
+
}
|
|
328
|
+
getDb() {
|
|
329
|
+
return this.db;
|
|
330
|
+
}
|
|
331
|
+
close() {
|
|
332
|
+
this.db.close();
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// ../storage/dist/repositories/conversation-repository.js
|
|
339
|
+
import crypto from "node:crypto";
|
|
340
|
+
var ConversationRepository;
|
|
341
|
+
var init_conversation_repository = __esm({
|
|
342
|
+
"../storage/dist/repositories/conversation-repository.js"() {
|
|
343
|
+
"use strict";
|
|
344
|
+
ConversationRepository = class {
|
|
345
|
+
db;
|
|
346
|
+
constructor(db) {
|
|
347
|
+
this.db = db;
|
|
348
|
+
}
|
|
349
|
+
create(platform, chatId, userId) {
|
|
350
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
351
|
+
const conversation = {
|
|
352
|
+
id: crypto.randomUUID(),
|
|
353
|
+
platform,
|
|
354
|
+
chatId,
|
|
355
|
+
userId,
|
|
356
|
+
createdAt: now,
|
|
357
|
+
updatedAt: now
|
|
358
|
+
};
|
|
359
|
+
this.db.prepare(`
|
|
360
|
+
INSERT INTO conversations (id, platform, chat_id, user_id, created_at, updated_at)
|
|
361
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
362
|
+
`).run(conversation.id, conversation.platform, conversation.chatId, conversation.userId, conversation.createdAt, conversation.updatedAt);
|
|
363
|
+
return conversation;
|
|
364
|
+
}
|
|
365
|
+
findById(id) {
|
|
366
|
+
const row = this.db.prepare("SELECT * FROM conversations WHERE id = ?").get(id);
|
|
367
|
+
if (!row)
|
|
368
|
+
return void 0;
|
|
369
|
+
return this.mapRow(row);
|
|
370
|
+
}
|
|
371
|
+
findByPlatformChat(platform, chatId) {
|
|
372
|
+
const row = this.db.prepare("SELECT * FROM conversations WHERE platform = ? AND chat_id = ?").get(platform, chatId);
|
|
373
|
+
if (!row)
|
|
374
|
+
return void 0;
|
|
375
|
+
return this.mapRow(row);
|
|
376
|
+
}
|
|
377
|
+
addMessage(conversationId, role, content, toolCalls) {
|
|
378
|
+
const message = {
|
|
379
|
+
id: crypto.randomUUID(),
|
|
380
|
+
conversationId,
|
|
381
|
+
role,
|
|
382
|
+
content,
|
|
383
|
+
toolCalls,
|
|
384
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
385
|
+
};
|
|
386
|
+
this.db.prepare(`
|
|
387
|
+
INSERT INTO messages (id, conversation_id, role, content, tool_calls, created_at)
|
|
388
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
389
|
+
`).run(message.id, message.conversationId, message.role, message.content, message.toolCalls ?? null, message.createdAt);
|
|
390
|
+
return message;
|
|
391
|
+
}
|
|
392
|
+
getMessages(conversationId, limit = 50) {
|
|
393
|
+
const rows = this.db.prepare("SELECT * FROM messages WHERE conversation_id = ? ORDER BY created_at ASC LIMIT ?").all(conversationId, limit);
|
|
394
|
+
return rows.map((row) => ({
|
|
395
|
+
id: row.id,
|
|
396
|
+
conversationId: row.conversation_id,
|
|
397
|
+
role: row.role,
|
|
398
|
+
content: row.content,
|
|
399
|
+
toolCalls: row.tool_calls ?? void 0,
|
|
400
|
+
createdAt: row.created_at
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
403
|
+
updateTimestamp(id) {
|
|
404
|
+
this.db.prepare("UPDATE conversations SET updated_at = ? WHERE id = ?").run((/* @__PURE__ */ new Date()).toISOString(), id);
|
|
405
|
+
}
|
|
406
|
+
mapRow(row) {
|
|
407
|
+
return {
|
|
408
|
+
id: row.id,
|
|
409
|
+
platform: row.platform,
|
|
410
|
+
chatId: row.chat_id,
|
|
411
|
+
userId: row.user_id,
|
|
412
|
+
createdAt: row.created_at,
|
|
413
|
+
updatedAt: row.updated_at
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// ../storage/dist/repositories/user-repository.js
|
|
421
|
+
import crypto2 from "node:crypto";
|
|
422
|
+
var UserRepository;
|
|
423
|
+
var init_user_repository = __esm({
|
|
424
|
+
"../storage/dist/repositories/user-repository.js"() {
|
|
425
|
+
"use strict";
|
|
426
|
+
UserRepository = class {
|
|
427
|
+
db;
|
|
428
|
+
constructor(db) {
|
|
429
|
+
this.db = db;
|
|
430
|
+
}
|
|
431
|
+
findOrCreate(platform, platformUserId, username, displayName) {
|
|
432
|
+
const existing = this.db.prepare("SELECT * FROM users WHERE platform = ? AND platform_user_id = ?").get(platform, platformUserId);
|
|
433
|
+
if (existing) {
|
|
434
|
+
return this.mapRow(existing);
|
|
435
|
+
}
|
|
436
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
437
|
+
const user = {
|
|
438
|
+
id: crypto2.randomUUID(),
|
|
439
|
+
platform,
|
|
440
|
+
platformUserId,
|
|
441
|
+
username,
|
|
442
|
+
displayName,
|
|
443
|
+
createdAt: now,
|
|
444
|
+
updatedAt: now
|
|
445
|
+
};
|
|
446
|
+
this.db.prepare(`
|
|
447
|
+
INSERT INTO users (id, platform, platform_user_id, username, display_name, created_at, updated_at)
|
|
448
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
449
|
+
`).run(user.id, user.platform, user.platformUserId, user.username ?? null, user.displayName ?? null, user.createdAt, user.updatedAt);
|
|
450
|
+
return user;
|
|
451
|
+
}
|
|
452
|
+
findById(id) {
|
|
453
|
+
const row = this.db.prepare("SELECT * FROM users WHERE id = ?").get(id);
|
|
454
|
+
if (!row)
|
|
455
|
+
return void 0;
|
|
456
|
+
return this.mapRow(row);
|
|
457
|
+
}
|
|
458
|
+
update(id, data) {
|
|
459
|
+
const fields = [];
|
|
460
|
+
const values = [];
|
|
461
|
+
if (data.username !== void 0) {
|
|
462
|
+
fields.push("username = ?");
|
|
463
|
+
values.push(data.username ?? null);
|
|
464
|
+
}
|
|
465
|
+
if (data.displayName !== void 0) {
|
|
466
|
+
fields.push("display_name = ?");
|
|
467
|
+
values.push(data.displayName ?? null);
|
|
468
|
+
}
|
|
469
|
+
if (fields.length === 0)
|
|
470
|
+
return;
|
|
471
|
+
fields.push("updated_at = ?");
|
|
472
|
+
values.push((/* @__PURE__ */ new Date()).toISOString());
|
|
473
|
+
values.push(id);
|
|
474
|
+
this.db.prepare(`UPDATE users SET ${fields.join(", ")} WHERE id = ?`).run(...values);
|
|
475
|
+
}
|
|
476
|
+
mapRow(row) {
|
|
477
|
+
return {
|
|
478
|
+
id: row.id,
|
|
479
|
+
platform: row.platform,
|
|
480
|
+
platformUserId: row.platform_user_id,
|
|
481
|
+
username: row.username ?? void 0,
|
|
482
|
+
displayName: row.display_name ?? void 0,
|
|
483
|
+
createdAt: row.created_at,
|
|
484
|
+
updatedAt: row.updated_at
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// ../storage/dist/repositories/audit-repository.js
|
|
492
|
+
var AuditRepository;
|
|
493
|
+
var init_audit_repository = __esm({
|
|
494
|
+
"../storage/dist/repositories/audit-repository.js"() {
|
|
495
|
+
"use strict";
|
|
496
|
+
AuditRepository = class {
|
|
497
|
+
db;
|
|
498
|
+
constructor(db) {
|
|
499
|
+
this.db = db;
|
|
500
|
+
}
|
|
501
|
+
log(entry) {
|
|
502
|
+
this.db.prepare(`
|
|
503
|
+
INSERT INTO audit_log (id, timestamp, user_id, action, risk_level, rule_id, effect, platform, chat_id, context)
|
|
504
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
505
|
+
`).run(entry.id, entry.timestamp.toISOString(), entry.userId, entry.action, entry.riskLevel, entry.ruleId ?? null, entry.effect, entry.platform, entry.chatId ?? null, entry.context ? JSON.stringify(entry.context) : null);
|
|
506
|
+
}
|
|
507
|
+
query(filters) {
|
|
508
|
+
const conditions = [];
|
|
509
|
+
const values = [];
|
|
510
|
+
if (filters.userId) {
|
|
511
|
+
conditions.push("user_id = ?");
|
|
512
|
+
values.push(filters.userId);
|
|
513
|
+
}
|
|
514
|
+
if (filters.action) {
|
|
515
|
+
conditions.push("action = ?");
|
|
516
|
+
values.push(filters.action);
|
|
517
|
+
}
|
|
518
|
+
if (filters.effect) {
|
|
519
|
+
conditions.push("effect = ?");
|
|
520
|
+
values.push(filters.effect);
|
|
521
|
+
}
|
|
522
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
523
|
+
const limit = filters.limit ?? 100;
|
|
524
|
+
values.push(limit);
|
|
525
|
+
const rows = this.db.prepare(`SELECT * FROM audit_log ${where} ORDER BY timestamp DESC LIMIT ?`).all(...values);
|
|
526
|
+
return rows.map((row) => this.mapRow(row));
|
|
527
|
+
}
|
|
528
|
+
count(filters) {
|
|
529
|
+
const conditions = [];
|
|
530
|
+
const values = [];
|
|
531
|
+
if (filters.userId) {
|
|
532
|
+
conditions.push("user_id = ?");
|
|
533
|
+
values.push(filters.userId);
|
|
534
|
+
}
|
|
535
|
+
if (filters.effect) {
|
|
536
|
+
conditions.push("effect = ?");
|
|
537
|
+
values.push(filters.effect);
|
|
538
|
+
}
|
|
539
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
540
|
+
const row = this.db.prepare(`SELECT COUNT(*) as count FROM audit_log ${where}`).get(...values);
|
|
541
|
+
return row.count;
|
|
542
|
+
}
|
|
543
|
+
mapRow(row) {
|
|
544
|
+
return {
|
|
545
|
+
id: row.id,
|
|
546
|
+
timestamp: new Date(row.timestamp),
|
|
547
|
+
userId: row.user_id,
|
|
548
|
+
action: row.action,
|
|
549
|
+
riskLevel: row.risk_level,
|
|
550
|
+
ruleId: row.rule_id ?? void 0,
|
|
551
|
+
effect: row.effect,
|
|
552
|
+
platform: row.platform,
|
|
553
|
+
chatId: row.chat_id ?? void 0,
|
|
554
|
+
context: row.context ? JSON.parse(row.context) : void 0
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// ../storage/dist/migrations/migrator.js
|
|
562
|
+
var init_migrator = __esm({
|
|
563
|
+
"../storage/dist/migrations/migrator.js"() {
|
|
564
|
+
"use strict";
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// ../storage/dist/migrations/index.js
|
|
569
|
+
var init_migrations = __esm({
|
|
570
|
+
"../storage/dist/migrations/index.js"() {
|
|
571
|
+
"use strict";
|
|
572
|
+
init_migrator();
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// ../storage/dist/index.js
|
|
577
|
+
var init_dist3 = __esm({
|
|
578
|
+
"../storage/dist/index.js"() {
|
|
579
|
+
"use strict";
|
|
580
|
+
init_database();
|
|
581
|
+
init_conversation_repository();
|
|
582
|
+
init_user_repository();
|
|
583
|
+
init_audit_repository();
|
|
584
|
+
init_migrator();
|
|
585
|
+
init_migrations();
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
// ../llm/dist/provider.js
|
|
590
|
+
var LLMProvider;
|
|
591
|
+
var init_provider = __esm({
|
|
592
|
+
"../llm/dist/provider.js"() {
|
|
593
|
+
"use strict";
|
|
594
|
+
LLMProvider = class {
|
|
595
|
+
config;
|
|
596
|
+
constructor(config) {
|
|
597
|
+
this.config = config;
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// ../llm/dist/providers/anthropic.js
|
|
604
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
605
|
+
var AnthropicProvider;
|
|
606
|
+
var init_anthropic = __esm({
|
|
607
|
+
"../llm/dist/providers/anthropic.js"() {
|
|
608
|
+
"use strict";
|
|
609
|
+
init_provider();
|
|
610
|
+
AnthropicProvider = class extends LLMProvider {
|
|
611
|
+
client;
|
|
612
|
+
constructor(config) {
|
|
613
|
+
super(config);
|
|
614
|
+
}
|
|
615
|
+
async initialize() {
|
|
616
|
+
this.client = new Anthropic({ apiKey: this.config.apiKey });
|
|
617
|
+
}
|
|
618
|
+
async complete(request) {
|
|
619
|
+
const messages = this.mapMessages(request.messages);
|
|
620
|
+
const tools = request.tools ? this.mapTools(request.tools) : void 0;
|
|
621
|
+
const params = {
|
|
622
|
+
model: this.config.model,
|
|
623
|
+
max_tokens: request.maxTokens ?? this.config.maxTokens ?? 4096,
|
|
624
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
625
|
+
system: request.system,
|
|
626
|
+
messages,
|
|
627
|
+
tools
|
|
628
|
+
};
|
|
629
|
+
const response = await this.client.messages.create(params);
|
|
630
|
+
return this.mapResponse(response);
|
|
631
|
+
}
|
|
632
|
+
async *stream(request) {
|
|
633
|
+
const messages = this.mapMessages(request.messages);
|
|
634
|
+
const tools = request.tools ? this.mapTools(request.tools) : void 0;
|
|
635
|
+
const stream = this.client.messages.stream({
|
|
636
|
+
model: this.config.model,
|
|
637
|
+
max_tokens: request.maxTokens ?? this.config.maxTokens ?? 4096,
|
|
638
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
639
|
+
system: request.system,
|
|
640
|
+
messages,
|
|
641
|
+
tools
|
|
642
|
+
});
|
|
643
|
+
for await (const event of stream) {
|
|
644
|
+
if (event.type === "content_block_delta") {
|
|
645
|
+
if (event.delta.type === "text_delta") {
|
|
646
|
+
yield { type: "text_delta", text: event.delta.text };
|
|
647
|
+
} else if (event.delta.type === "input_json_delta") {
|
|
648
|
+
yield {
|
|
649
|
+
type: "tool_use_delta",
|
|
650
|
+
toolCall: { input: event.delta.partial_json }
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
} else if (event.type === "content_block_start") {
|
|
654
|
+
if (event.content_block.type === "tool_use") {
|
|
655
|
+
yield {
|
|
656
|
+
type: "tool_use_start",
|
|
657
|
+
toolCall: {
|
|
658
|
+
id: event.content_block.id,
|
|
659
|
+
name: event.content_block.name
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
} else if (event.type === "message_stop") {
|
|
664
|
+
const finalMessage = await stream.finalMessage();
|
|
665
|
+
yield {
|
|
666
|
+
type: "message_complete",
|
|
667
|
+
response: this.mapResponse(finalMessage)
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
isAvailable() {
|
|
673
|
+
return !!this.config.apiKey;
|
|
674
|
+
}
|
|
675
|
+
mapMessages(messages) {
|
|
676
|
+
return messages.map((msg) => {
|
|
677
|
+
if (typeof msg.content === "string") {
|
|
678
|
+
return { role: msg.role, content: msg.content };
|
|
679
|
+
}
|
|
680
|
+
const blocks = msg.content.map((block) => {
|
|
681
|
+
switch (block.type) {
|
|
682
|
+
case "text":
|
|
683
|
+
return { type: "text", text: block.text };
|
|
684
|
+
case "tool_use":
|
|
685
|
+
return {
|
|
686
|
+
type: "tool_use",
|
|
687
|
+
id: block.id,
|
|
688
|
+
name: block.name,
|
|
689
|
+
input: block.input
|
|
690
|
+
};
|
|
691
|
+
case "tool_result":
|
|
692
|
+
return {
|
|
693
|
+
type: "tool_result",
|
|
694
|
+
tool_use_id: block.tool_use_id,
|
|
695
|
+
content: block.content,
|
|
696
|
+
is_error: block.is_error
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
return { role: msg.role, content: blocks };
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
mapTools(tools) {
|
|
704
|
+
return tools.map((tool) => ({
|
|
705
|
+
name: tool.name,
|
|
706
|
+
description: tool.description,
|
|
707
|
+
input_schema: tool.inputSchema
|
|
708
|
+
}));
|
|
709
|
+
}
|
|
710
|
+
mapResponse(response) {
|
|
711
|
+
let textContent = "";
|
|
712
|
+
const toolCalls = [];
|
|
713
|
+
for (const block of response.content) {
|
|
714
|
+
if (block.type === "text") {
|
|
715
|
+
textContent += block.text;
|
|
716
|
+
} else if (block.type === "tool_use") {
|
|
717
|
+
toolCalls.push({
|
|
718
|
+
id: block.id,
|
|
719
|
+
name: block.name,
|
|
720
|
+
input: block.input
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return {
|
|
725
|
+
content: textContent,
|
|
726
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
727
|
+
usage: {
|
|
728
|
+
inputTokens: response.usage.input_tokens,
|
|
729
|
+
outputTokens: response.usage.output_tokens
|
|
730
|
+
},
|
|
731
|
+
stopReason: response.stop_reason
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
// ../llm/dist/providers/openai.js
|
|
739
|
+
import OpenAI from "openai";
|
|
740
|
+
var OpenAIProvider;
|
|
741
|
+
var init_openai = __esm({
|
|
742
|
+
"../llm/dist/providers/openai.js"() {
|
|
743
|
+
"use strict";
|
|
744
|
+
init_provider();
|
|
745
|
+
OpenAIProvider = class extends LLMProvider {
|
|
746
|
+
client;
|
|
747
|
+
constructor(config) {
|
|
748
|
+
super(config);
|
|
749
|
+
}
|
|
750
|
+
async initialize() {
|
|
751
|
+
this.client = new OpenAI({
|
|
752
|
+
apiKey: this.config.apiKey,
|
|
753
|
+
baseURL: this.config.baseUrl
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
async complete(request) {
|
|
757
|
+
const messages = this.mapMessages(request.messages, request.system);
|
|
758
|
+
const tools = request.tools ? this.mapTools(request.tools) : void 0;
|
|
759
|
+
const params = {
|
|
760
|
+
model: this.config.model,
|
|
761
|
+
max_tokens: request.maxTokens ?? this.config.maxTokens ?? 4096,
|
|
762
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
763
|
+
messages,
|
|
764
|
+
...tools ? { tools } : {}
|
|
765
|
+
};
|
|
766
|
+
const response = await this.client.chat.completions.create(params);
|
|
767
|
+
return this.mapResponse(response);
|
|
768
|
+
}
|
|
769
|
+
async *stream(request) {
|
|
770
|
+
const messages = this.mapMessages(request.messages, request.system);
|
|
771
|
+
const tools = request.tools ? this.mapTools(request.tools) : void 0;
|
|
772
|
+
const stream = await this.client.chat.completions.create({
|
|
773
|
+
model: this.config.model,
|
|
774
|
+
max_tokens: request.maxTokens ?? this.config.maxTokens ?? 4096,
|
|
775
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
776
|
+
messages,
|
|
777
|
+
...tools ? { tools } : {},
|
|
778
|
+
stream: true
|
|
779
|
+
});
|
|
780
|
+
let currentToolCallId;
|
|
781
|
+
let currentToolCallName;
|
|
782
|
+
let currentToolCallArgs = "";
|
|
783
|
+
let fullContent = "";
|
|
784
|
+
const toolCalls = [];
|
|
785
|
+
let finishReason = null;
|
|
786
|
+
let promptTokens = 0;
|
|
787
|
+
let completionTokens = 0;
|
|
788
|
+
for await (const chunk of stream) {
|
|
789
|
+
const choice = chunk.choices[0];
|
|
790
|
+
if (!choice)
|
|
791
|
+
continue;
|
|
792
|
+
const delta = choice.delta;
|
|
793
|
+
if (delta?.content) {
|
|
794
|
+
fullContent += delta.content;
|
|
795
|
+
yield { type: "text_delta", text: delta.content };
|
|
796
|
+
}
|
|
797
|
+
if (delta?.tool_calls) {
|
|
798
|
+
for (const toolCallDelta of delta.tool_calls) {
|
|
799
|
+
if (toolCallDelta.id) {
|
|
800
|
+
if (currentToolCallId) {
|
|
801
|
+
toolCalls.push({
|
|
802
|
+
id: currentToolCallId,
|
|
803
|
+
name: currentToolCallName,
|
|
804
|
+
input: JSON.parse(currentToolCallArgs || "{}")
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
currentToolCallId = toolCallDelta.id;
|
|
808
|
+
currentToolCallName = toolCallDelta.function?.name;
|
|
809
|
+
currentToolCallArgs = toolCallDelta.function?.arguments ?? "";
|
|
810
|
+
yield {
|
|
811
|
+
type: "tool_use_start",
|
|
812
|
+
toolCall: {
|
|
813
|
+
id: currentToolCallId,
|
|
814
|
+
name: currentToolCallName
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
} else if (toolCallDelta.function?.arguments) {
|
|
818
|
+
currentToolCallArgs += toolCallDelta.function.arguments;
|
|
819
|
+
yield {
|
|
820
|
+
type: "tool_use_delta",
|
|
821
|
+
toolCall: {
|
|
822
|
+
input: toolCallDelta.function.arguments
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
if (choice.finish_reason) {
|
|
829
|
+
finishReason = choice.finish_reason;
|
|
830
|
+
}
|
|
831
|
+
if (chunk.usage) {
|
|
832
|
+
promptTokens = chunk.usage.prompt_tokens;
|
|
833
|
+
completionTokens = chunk.usage.completion_tokens;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
if (currentToolCallId) {
|
|
837
|
+
toolCalls.push({
|
|
838
|
+
id: currentToolCallId,
|
|
839
|
+
name: currentToolCallName,
|
|
840
|
+
input: JSON.parse(currentToolCallArgs || "{}")
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
yield {
|
|
844
|
+
type: "message_complete",
|
|
845
|
+
response: {
|
|
846
|
+
content: fullContent,
|
|
847
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
848
|
+
usage: {
|
|
849
|
+
inputTokens: promptTokens,
|
|
850
|
+
outputTokens: completionTokens
|
|
851
|
+
},
|
|
852
|
+
stopReason: this.mapStopReason(finishReason)
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
isAvailable() {
|
|
857
|
+
return !!this.config.apiKey;
|
|
858
|
+
}
|
|
859
|
+
mapMessages(messages, system) {
|
|
860
|
+
const mapped = [];
|
|
861
|
+
if (system) {
|
|
862
|
+
mapped.push({ role: "system", content: system });
|
|
863
|
+
}
|
|
864
|
+
for (const msg of messages) {
|
|
865
|
+
if (typeof msg.content === "string") {
|
|
866
|
+
mapped.push({ role: msg.role, content: msg.content });
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
const textParts = [];
|
|
870
|
+
const toolUseParts = [];
|
|
871
|
+
const toolResultParts = [];
|
|
872
|
+
for (const block of msg.content) {
|
|
873
|
+
switch (block.type) {
|
|
874
|
+
case "text":
|
|
875
|
+
textParts.push({ type: "text", text: block.text });
|
|
876
|
+
break;
|
|
877
|
+
case "tool_use":
|
|
878
|
+
toolUseParts.push({
|
|
879
|
+
id: block.id,
|
|
880
|
+
type: "function",
|
|
881
|
+
function: {
|
|
882
|
+
name: block.name,
|
|
883
|
+
arguments: JSON.stringify(block.input)
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
break;
|
|
887
|
+
case "tool_result":
|
|
888
|
+
toolResultParts.push({
|
|
889
|
+
tool_call_id: block.tool_use_id,
|
|
890
|
+
content: block.content
|
|
891
|
+
});
|
|
892
|
+
break;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
if (msg.role === "assistant" && toolUseParts.length > 0) {
|
|
896
|
+
const textContent = textParts.map((p) => p.text).join("");
|
|
897
|
+
mapped.push({
|
|
898
|
+
role: "assistant",
|
|
899
|
+
content: textContent || null,
|
|
900
|
+
tool_calls: toolUseParts
|
|
901
|
+
});
|
|
902
|
+
} else if (toolResultParts.length > 0) {
|
|
903
|
+
for (const result of toolResultParts) {
|
|
904
|
+
mapped.push({
|
|
905
|
+
role: "tool",
|
|
906
|
+
tool_call_id: result.tool_call_id,
|
|
907
|
+
content: result.content
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
} else if (textParts.length > 0) {
|
|
911
|
+
if (msg.role === "user") {
|
|
912
|
+
mapped.push({ role: "user", content: textParts });
|
|
913
|
+
} else {
|
|
914
|
+
mapped.push({ role: msg.role, content: textParts.map((p) => p.text).join("") });
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
return mapped;
|
|
919
|
+
}
|
|
920
|
+
mapTools(tools) {
|
|
921
|
+
return tools.map((tool) => ({
|
|
922
|
+
type: "function",
|
|
923
|
+
function: {
|
|
924
|
+
name: tool.name,
|
|
925
|
+
description: tool.description,
|
|
926
|
+
parameters: tool.inputSchema
|
|
927
|
+
}
|
|
928
|
+
}));
|
|
929
|
+
}
|
|
930
|
+
mapResponse(response) {
|
|
931
|
+
const choice = response.choices[0];
|
|
932
|
+
const message = choice?.message;
|
|
933
|
+
const content = message?.content ?? "";
|
|
934
|
+
const toolCalls = message?.tool_calls?.map((tc) => ({
|
|
935
|
+
id: tc.id,
|
|
936
|
+
name: tc.function.name,
|
|
937
|
+
input: JSON.parse(tc.function.arguments)
|
|
938
|
+
}));
|
|
939
|
+
return {
|
|
940
|
+
content,
|
|
941
|
+
toolCalls: toolCalls && toolCalls.length > 0 ? toolCalls : void 0,
|
|
942
|
+
usage: {
|
|
943
|
+
inputTokens: response.usage?.prompt_tokens ?? 0,
|
|
944
|
+
outputTokens: response.usage?.completion_tokens ?? 0
|
|
945
|
+
},
|
|
946
|
+
stopReason: this.mapStopReason(choice?.finish_reason ?? null)
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
mapStopReason(finishReason) {
|
|
950
|
+
switch (finishReason) {
|
|
951
|
+
case "stop":
|
|
952
|
+
return "end_turn";
|
|
953
|
+
case "tool_calls":
|
|
954
|
+
return "tool_use";
|
|
955
|
+
case "length":
|
|
956
|
+
return "max_tokens";
|
|
957
|
+
default:
|
|
958
|
+
return "end_turn";
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
// ../llm/dist/providers/openrouter.js
|
|
966
|
+
var OpenRouterProvider;
|
|
967
|
+
var init_openrouter = __esm({
|
|
968
|
+
"../llm/dist/providers/openrouter.js"() {
|
|
969
|
+
"use strict";
|
|
970
|
+
init_openai();
|
|
971
|
+
OpenRouterProvider = class extends OpenAIProvider {
|
|
972
|
+
constructor(config) {
|
|
973
|
+
super({
|
|
974
|
+
...config,
|
|
975
|
+
baseUrl: config.baseUrl ?? "https://openrouter.ai/api/v1"
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
isAvailable() {
|
|
979
|
+
return !!this.config.apiKey;
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
// ../llm/dist/providers/ollama.js
|
|
986
|
+
var OllamaProvider;
|
|
987
|
+
var init_ollama = __esm({
|
|
988
|
+
"../llm/dist/providers/ollama.js"() {
|
|
989
|
+
"use strict";
|
|
990
|
+
init_provider();
|
|
991
|
+
OllamaProvider = class extends LLMProvider {
|
|
992
|
+
baseUrl = "";
|
|
993
|
+
constructor(config) {
|
|
994
|
+
super(config);
|
|
995
|
+
}
|
|
996
|
+
async initialize() {
|
|
997
|
+
this.baseUrl = this.config.baseUrl ?? "http://localhost:11434";
|
|
998
|
+
}
|
|
999
|
+
async complete(request) {
|
|
1000
|
+
const messages = this.buildMessages(request.messages, request.system);
|
|
1001
|
+
const tools = request.tools ? this.mapTools(request.tools) : void 0;
|
|
1002
|
+
const body = {
|
|
1003
|
+
model: this.config.model,
|
|
1004
|
+
messages,
|
|
1005
|
+
stream: false,
|
|
1006
|
+
options: this.buildOptions(request)
|
|
1007
|
+
};
|
|
1008
|
+
if (tools && tools.length > 0) {
|
|
1009
|
+
body.tools = tools;
|
|
1010
|
+
}
|
|
1011
|
+
const res = await fetch(`${this.baseUrl}/api/chat`, {
|
|
1012
|
+
method: "POST",
|
|
1013
|
+
headers: { "Content-Type": "application/json" },
|
|
1014
|
+
body: JSON.stringify(body)
|
|
1015
|
+
});
|
|
1016
|
+
if (!res.ok) {
|
|
1017
|
+
const errorText = await res.text();
|
|
1018
|
+
throw new Error(`Ollama API error (${res.status}): ${errorText}`);
|
|
1019
|
+
}
|
|
1020
|
+
const data = await res.json();
|
|
1021
|
+
return this.mapResponse(data);
|
|
1022
|
+
}
|
|
1023
|
+
async *stream(request) {
|
|
1024
|
+
const messages = this.buildMessages(request.messages, request.system);
|
|
1025
|
+
const tools = request.tools ? this.mapTools(request.tools) : void 0;
|
|
1026
|
+
const body = {
|
|
1027
|
+
model: this.config.model,
|
|
1028
|
+
messages,
|
|
1029
|
+
stream: true,
|
|
1030
|
+
options: this.buildOptions(request)
|
|
1031
|
+
};
|
|
1032
|
+
if (tools && tools.length > 0) {
|
|
1033
|
+
body.tools = tools;
|
|
1034
|
+
}
|
|
1035
|
+
const res = await fetch(`${this.baseUrl}/api/chat`, {
|
|
1036
|
+
method: "POST",
|
|
1037
|
+
headers: { "Content-Type": "application/json" },
|
|
1038
|
+
body: JSON.stringify(body)
|
|
1039
|
+
});
|
|
1040
|
+
if (!res.ok) {
|
|
1041
|
+
const errorText = await res.text();
|
|
1042
|
+
throw new Error(`Ollama API error (${res.status}): ${errorText}`);
|
|
1043
|
+
}
|
|
1044
|
+
if (!res.body) {
|
|
1045
|
+
throw new Error("Ollama streaming response has no body");
|
|
1046
|
+
}
|
|
1047
|
+
const reader = res.body.getReader();
|
|
1048
|
+
const decoder = new TextDecoder();
|
|
1049
|
+
let buffer = "";
|
|
1050
|
+
let fullContent = "";
|
|
1051
|
+
let promptEvalCount = 0;
|
|
1052
|
+
let evalCount = 0;
|
|
1053
|
+
const toolCalls = [];
|
|
1054
|
+
try {
|
|
1055
|
+
while (true) {
|
|
1056
|
+
const { done, value } = await reader.read();
|
|
1057
|
+
if (done)
|
|
1058
|
+
break;
|
|
1059
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1060
|
+
const lines = buffer.split("\n");
|
|
1061
|
+
buffer = lines.pop() ?? "";
|
|
1062
|
+
for (const line of lines) {
|
|
1063
|
+
const trimmed = line.trim();
|
|
1064
|
+
if (!trimmed)
|
|
1065
|
+
continue;
|
|
1066
|
+
let chunk;
|
|
1067
|
+
try {
|
|
1068
|
+
chunk = JSON.parse(trimmed);
|
|
1069
|
+
} catch {
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
if (chunk.message?.content) {
|
|
1073
|
+
fullContent += chunk.message.content;
|
|
1074
|
+
yield { type: "text_delta", text: chunk.message.content };
|
|
1075
|
+
}
|
|
1076
|
+
if (chunk.message?.tool_calls) {
|
|
1077
|
+
for (const tc of chunk.message.tool_calls) {
|
|
1078
|
+
const toolCall = {
|
|
1079
|
+
id: `ollama_tool_${toolCalls.length}`,
|
|
1080
|
+
name: tc.function.name,
|
|
1081
|
+
input: tc.function.arguments
|
|
1082
|
+
};
|
|
1083
|
+
toolCalls.push(toolCall);
|
|
1084
|
+
yield {
|
|
1085
|
+
type: "tool_use_start",
|
|
1086
|
+
toolCall: { id: toolCall.id, name: toolCall.name }
|
|
1087
|
+
};
|
|
1088
|
+
yield {
|
|
1089
|
+
type: "tool_use_delta",
|
|
1090
|
+
toolCall: { input: toolCall.input }
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
if (chunk.done) {
|
|
1095
|
+
promptEvalCount = chunk.prompt_eval_count ?? 0;
|
|
1096
|
+
evalCount = chunk.eval_count ?? 0;
|
|
1097
|
+
yield {
|
|
1098
|
+
type: "message_complete",
|
|
1099
|
+
response: {
|
|
1100
|
+
content: fullContent,
|
|
1101
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
1102
|
+
usage: {
|
|
1103
|
+
inputTokens: promptEvalCount,
|
|
1104
|
+
outputTokens: evalCount
|
|
1105
|
+
},
|
|
1106
|
+
stopReason: toolCalls.length > 0 ? "tool_use" : "end_turn"
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
if (buffer.trim()) {
|
|
1113
|
+
let chunk;
|
|
1114
|
+
try {
|
|
1115
|
+
chunk = JSON.parse(buffer.trim());
|
|
1116
|
+
} catch {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
if (chunk.message?.content) {
|
|
1120
|
+
fullContent += chunk.message.content;
|
|
1121
|
+
yield { type: "text_delta", text: chunk.message.content };
|
|
1122
|
+
}
|
|
1123
|
+
if (chunk.message?.tool_calls) {
|
|
1124
|
+
for (const tc of chunk.message.tool_calls) {
|
|
1125
|
+
const toolCall = {
|
|
1126
|
+
id: `ollama_tool_${toolCalls.length}`,
|
|
1127
|
+
name: tc.function.name,
|
|
1128
|
+
input: tc.function.arguments
|
|
1129
|
+
};
|
|
1130
|
+
toolCalls.push(toolCall);
|
|
1131
|
+
yield {
|
|
1132
|
+
type: "tool_use_start",
|
|
1133
|
+
toolCall: { id: toolCall.id, name: toolCall.name }
|
|
1134
|
+
};
|
|
1135
|
+
yield {
|
|
1136
|
+
type: "tool_use_delta",
|
|
1137
|
+
toolCall: { input: toolCall.input }
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
if (chunk.done) {
|
|
1142
|
+
promptEvalCount = chunk.prompt_eval_count ?? 0;
|
|
1143
|
+
evalCount = chunk.eval_count ?? 0;
|
|
1144
|
+
yield {
|
|
1145
|
+
type: "message_complete",
|
|
1146
|
+
response: {
|
|
1147
|
+
content: fullContent,
|
|
1148
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
1149
|
+
usage: {
|
|
1150
|
+
inputTokens: promptEvalCount,
|
|
1151
|
+
outputTokens: evalCount
|
|
1152
|
+
},
|
|
1153
|
+
stopReason: toolCalls.length > 0 ? "tool_use" : "end_turn"
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
} finally {
|
|
1159
|
+
reader.releaseLock();
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
isAvailable() {
|
|
1163
|
+
try {
|
|
1164
|
+
return this.baseUrl.length > 0;
|
|
1165
|
+
} catch {
|
|
1166
|
+
return false;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
buildOptions(request) {
|
|
1170
|
+
const options = {};
|
|
1171
|
+
const temperature = request.temperature ?? this.config.temperature;
|
|
1172
|
+
if (temperature !== void 0) {
|
|
1173
|
+
options.temperature = temperature;
|
|
1174
|
+
}
|
|
1175
|
+
const maxTokens = request.maxTokens ?? this.config.maxTokens;
|
|
1176
|
+
if (maxTokens !== void 0) {
|
|
1177
|
+
options.num_predict = maxTokens;
|
|
1178
|
+
}
|
|
1179
|
+
return options;
|
|
1180
|
+
}
|
|
1181
|
+
buildMessages(messages, system) {
|
|
1182
|
+
const mapped = [];
|
|
1183
|
+
if (system) {
|
|
1184
|
+
mapped.push({ role: "system", content: system });
|
|
1185
|
+
}
|
|
1186
|
+
for (const msg of messages) {
|
|
1187
|
+
if (typeof msg.content === "string") {
|
|
1188
|
+
mapped.push({ role: msg.role, content: msg.content });
|
|
1189
|
+
} else {
|
|
1190
|
+
mapped.push(this.mapContentBlocks(msg.role, msg.content));
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
return mapped;
|
|
1194
|
+
}
|
|
1195
|
+
mapContentBlocks(role, blocks) {
|
|
1196
|
+
const textParts = [];
|
|
1197
|
+
for (const block of blocks) {
|
|
1198
|
+
switch (block.type) {
|
|
1199
|
+
case "text":
|
|
1200
|
+
textParts.push(block.text);
|
|
1201
|
+
break;
|
|
1202
|
+
case "tool_use":
|
|
1203
|
+
textParts.push(`[Tool call: ${block.name}(${JSON.stringify(block.input)})]`);
|
|
1204
|
+
break;
|
|
1205
|
+
case "tool_result":
|
|
1206
|
+
textParts.push(`[Tool result for ${block.tool_use_id}]: ${block.content}`);
|
|
1207
|
+
break;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
return { role, content: textParts.join("\n") };
|
|
1211
|
+
}
|
|
1212
|
+
mapTools(tools) {
|
|
1213
|
+
return tools.map((tool) => ({
|
|
1214
|
+
type: "function",
|
|
1215
|
+
function: {
|
|
1216
|
+
name: tool.name,
|
|
1217
|
+
description: tool.description,
|
|
1218
|
+
parameters: tool.inputSchema
|
|
1219
|
+
}
|
|
1220
|
+
}));
|
|
1221
|
+
}
|
|
1222
|
+
mapResponse(data) {
|
|
1223
|
+
const toolCalls = [];
|
|
1224
|
+
if (data.message.tool_calls) {
|
|
1225
|
+
for (const tc of data.message.tool_calls) {
|
|
1226
|
+
toolCalls.push({
|
|
1227
|
+
id: `ollama_tool_${toolCalls.length}`,
|
|
1228
|
+
name: tc.function.name,
|
|
1229
|
+
input: tc.function.arguments
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return {
|
|
1234
|
+
content: data.message.content,
|
|
1235
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
1236
|
+
usage: {
|
|
1237
|
+
inputTokens: data.prompt_eval_count ?? 0,
|
|
1238
|
+
outputTokens: data.eval_count ?? 0
|
|
1239
|
+
},
|
|
1240
|
+
stopReason: toolCalls.length > 0 ? "tool_use" : "end_turn"
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
// ../llm/dist/provider-factory.js
|
|
1248
|
+
function createLLMProvider(config) {
|
|
1249
|
+
switch (config.provider) {
|
|
1250
|
+
case "anthropic":
|
|
1251
|
+
return new AnthropicProvider(config);
|
|
1252
|
+
case "openai":
|
|
1253
|
+
return new OpenAIProvider(config);
|
|
1254
|
+
case "openrouter":
|
|
1255
|
+
return new OpenRouterProvider(config);
|
|
1256
|
+
case "ollama":
|
|
1257
|
+
return new OllamaProvider(config);
|
|
1258
|
+
default:
|
|
1259
|
+
throw new Error(`Unknown LLM provider: ${config.provider}`);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
var init_provider_factory = __esm({
|
|
1263
|
+
"../llm/dist/provider-factory.js"() {
|
|
1264
|
+
"use strict";
|
|
1265
|
+
init_anthropic();
|
|
1266
|
+
init_openai();
|
|
1267
|
+
init_openrouter();
|
|
1268
|
+
init_ollama();
|
|
1269
|
+
}
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
// ../llm/dist/prompt-builder.js
|
|
1273
|
+
var PromptBuilder;
|
|
1274
|
+
var init_prompt_builder = __esm({
|
|
1275
|
+
"../llm/dist/prompt-builder.js"() {
|
|
1276
|
+
"use strict";
|
|
1277
|
+
PromptBuilder = class {
|
|
1278
|
+
buildSystemPrompt() {
|
|
1279
|
+
return "You are Alfred, a personal AI assistant. You are helpful, precise, and security-conscious. You have access to various tools (skills) that you can use to help the user. Always explain what you are doing before using a tool. Be concise but thorough.";
|
|
1280
|
+
}
|
|
1281
|
+
buildMessages(history) {
|
|
1282
|
+
return history.filter((msg) => msg.role === "user" || msg.role === "assistant").map((msg) => {
|
|
1283
|
+
if (msg.toolCalls) {
|
|
1284
|
+
const toolCalls = JSON.parse(msg.toolCalls);
|
|
1285
|
+
const content = [];
|
|
1286
|
+
if (msg.content) {
|
|
1287
|
+
content.push({ type: "text", text: msg.content });
|
|
1288
|
+
}
|
|
1289
|
+
for (const tc of toolCalls) {
|
|
1290
|
+
content.push({
|
|
1291
|
+
type: "tool_use",
|
|
1292
|
+
id: tc.id,
|
|
1293
|
+
name: tc.name,
|
|
1294
|
+
input: tc.input
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
return { role: msg.role, content };
|
|
1298
|
+
}
|
|
1299
|
+
return { role: msg.role, content: msg.content };
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
buildTools(skills) {
|
|
1303
|
+
return skills.map((skill) => ({
|
|
1304
|
+
name: skill.name,
|
|
1305
|
+
description: skill.description,
|
|
1306
|
+
inputSchema: skill.inputSchema
|
|
1307
|
+
}));
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
// ../llm/dist/index.js
|
|
1314
|
+
var init_dist4 = __esm({
|
|
1315
|
+
"../llm/dist/index.js"() {
|
|
1316
|
+
"use strict";
|
|
1317
|
+
init_provider();
|
|
1318
|
+
init_anthropic();
|
|
1319
|
+
init_openai();
|
|
1320
|
+
init_openrouter();
|
|
1321
|
+
init_ollama();
|
|
1322
|
+
init_provider_factory();
|
|
1323
|
+
init_prompt_builder();
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
// ../security/dist/rate-limiter.js
|
|
1328
|
+
var RateLimiter;
|
|
1329
|
+
var init_rate_limiter = __esm({
|
|
1330
|
+
"../security/dist/rate-limiter.js"() {
|
|
1331
|
+
"use strict";
|
|
1332
|
+
RateLimiter = class {
|
|
1333
|
+
buckets = /* @__PURE__ */ new Map();
|
|
1334
|
+
check(key, limit) {
|
|
1335
|
+
const now = Date.now();
|
|
1336
|
+
const windowMs = limit.windowSeconds * 1e3;
|
|
1337
|
+
const bucket = this.buckets.get(key);
|
|
1338
|
+
if (!bucket) {
|
|
1339
|
+
return {
|
|
1340
|
+
allowed: true,
|
|
1341
|
+
remaining: limit.maxInvocations,
|
|
1342
|
+
resetsAt: now + windowMs
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
if (now > bucket.windowStart + windowMs) {
|
|
1346
|
+
return {
|
|
1347
|
+
allowed: true,
|
|
1348
|
+
remaining: limit.maxInvocations,
|
|
1349
|
+
resetsAt: now + windowMs
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
const remaining = Math.max(0, limit.maxInvocations - bucket.count);
|
|
1353
|
+
return {
|
|
1354
|
+
allowed: bucket.count < limit.maxInvocations,
|
|
1355
|
+
remaining,
|
|
1356
|
+
resetsAt: bucket.windowStart + windowMs
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
increment(key, limit) {
|
|
1360
|
+
const now = Date.now();
|
|
1361
|
+
const windowMs = limit.windowSeconds * 1e3;
|
|
1362
|
+
const bucket = this.buckets.get(key);
|
|
1363
|
+
if (!bucket || now > bucket.windowStart + windowMs) {
|
|
1364
|
+
this.buckets.set(key, { count: 1, windowStart: now });
|
|
1365
|
+
} else {
|
|
1366
|
+
bucket.count += 1;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
reset() {
|
|
1370
|
+
this.buckets.clear();
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
// ../security/dist/rule-engine.js
|
|
1377
|
+
var RuleEngine;
|
|
1378
|
+
var init_rule_engine = __esm({
|
|
1379
|
+
"../security/dist/rule-engine.js"() {
|
|
1380
|
+
"use strict";
|
|
1381
|
+
init_rate_limiter();
|
|
1382
|
+
RuleEngine = class {
|
|
1383
|
+
rules = [];
|
|
1384
|
+
rateLimiter = new RateLimiter();
|
|
1385
|
+
loadRules(rules) {
|
|
1386
|
+
this.rules = [...rules].sort((a, b) => a.priority - b.priority);
|
|
1387
|
+
}
|
|
1388
|
+
getRules() {
|
|
1389
|
+
return this.rules;
|
|
1390
|
+
}
|
|
1391
|
+
evaluate(context) {
|
|
1392
|
+
for (const rule of this.rules) {
|
|
1393
|
+
if (this.ruleMatches(rule, context)) {
|
|
1394
|
+
if (rule.rateLimit && rule.effect === "allow") {
|
|
1395
|
+
const rateLimitResult = this.checkRateLimit(rule, context);
|
|
1396
|
+
if (!rateLimitResult) {
|
|
1397
|
+
return {
|
|
1398
|
+
allowed: false,
|
|
1399
|
+
matchedRule: rule,
|
|
1400
|
+
reason: `Rate limit exceeded for rule: ${rule.id}`,
|
|
1401
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
return {
|
|
1406
|
+
allowed: rule.effect === "allow",
|
|
1407
|
+
matchedRule: rule,
|
|
1408
|
+
reason: `Matched rule: ${rule.id}`,
|
|
1409
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
return {
|
|
1414
|
+
allowed: false,
|
|
1415
|
+
matchedRule: void 0,
|
|
1416
|
+
reason: "No matching rule found \u2014 default deny",
|
|
1417
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Checks and increments the rate limit counter for a given rule and context.
|
|
1422
|
+
* Returns true if the action is within the rate limit, false if exceeded.
|
|
1423
|
+
*/
|
|
1424
|
+
checkRateLimit(rule, context) {
|
|
1425
|
+
if (!rule.rateLimit) {
|
|
1426
|
+
return true;
|
|
1427
|
+
}
|
|
1428
|
+
const scopeKey = this.getScopeKey(rule.scope, context);
|
|
1429
|
+
const key = `${rule.id}:${scopeKey}`;
|
|
1430
|
+
const result = this.rateLimiter.check(key, rule.rateLimit);
|
|
1431
|
+
if (!result.allowed) {
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
this.rateLimiter.increment(key, rule.rateLimit);
|
|
1435
|
+
return true;
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Resets all rate limit counters. Useful for testing.
|
|
1439
|
+
*/
|
|
1440
|
+
resetRateLimits() {
|
|
1441
|
+
this.rateLimiter.reset();
|
|
1442
|
+
}
|
|
1443
|
+
getScopeKey(scope, context) {
|
|
1444
|
+
switch (scope) {
|
|
1445
|
+
case "global":
|
|
1446
|
+
return "global";
|
|
1447
|
+
case "user":
|
|
1448
|
+
return context.userId;
|
|
1449
|
+
case "conversation":
|
|
1450
|
+
return context.chatId ?? "unknown";
|
|
1451
|
+
case "platform":
|
|
1452
|
+
return context.platform;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
ruleMatches(rule, context) {
|
|
1456
|
+
if (!rule.actions.includes("*") && !rule.actions.includes(context.action)) {
|
|
1457
|
+
return false;
|
|
1458
|
+
}
|
|
1459
|
+
if (!rule.riskLevels.includes(context.riskLevel)) {
|
|
1460
|
+
return false;
|
|
1461
|
+
}
|
|
1462
|
+
if (rule.conditions) {
|
|
1463
|
+
if (rule.conditions.users && rule.conditions.users.length > 0) {
|
|
1464
|
+
if (!rule.conditions.users.includes(context.userId)) {
|
|
1465
|
+
return false;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
if (rule.conditions.platforms && rule.conditions.platforms.length > 0) {
|
|
1469
|
+
if (!rule.conditions.platforms.includes(context.platform)) {
|
|
1470
|
+
return false;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
if (rule.conditions.chatType && context.chatType) {
|
|
1474
|
+
if (rule.conditions.chatType !== context.chatType) {
|
|
1475
|
+
return false;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
if (rule.conditions.timeWindow) {
|
|
1479
|
+
if (!this.matchesTimeWindow(rule.conditions.timeWindow)) {
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
return true;
|
|
1485
|
+
}
|
|
1486
|
+
matchesTimeWindow(timeWindow) {
|
|
1487
|
+
if (!timeWindow) {
|
|
1488
|
+
return true;
|
|
1489
|
+
}
|
|
1490
|
+
const now = /* @__PURE__ */ new Date();
|
|
1491
|
+
if (timeWindow.daysOfWeek && timeWindow.daysOfWeek.length > 0) {
|
|
1492
|
+
if (!timeWindow.daysOfWeek.includes(now.getDay())) {
|
|
1493
|
+
return false;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
const currentHour = now.getHours();
|
|
1497
|
+
if (timeWindow.startHour !== void 0 && timeWindow.endHour !== void 0) {
|
|
1498
|
+
if (timeWindow.startHour <= timeWindow.endHour) {
|
|
1499
|
+
if (currentHour < timeWindow.startHour || currentHour >= timeWindow.endHour) {
|
|
1500
|
+
return false;
|
|
1501
|
+
}
|
|
1502
|
+
} else {
|
|
1503
|
+
if (currentHour < timeWindow.startHour && currentHour >= timeWindow.endHour) {
|
|
1504
|
+
return false;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
} else if (timeWindow.startHour !== void 0) {
|
|
1508
|
+
if (currentHour < timeWindow.startHour) {
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
} else if (timeWindow.endHour !== void 0) {
|
|
1512
|
+
if (currentHour >= timeWindow.endHour) {
|
|
1513
|
+
return false;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
return true;
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
// ../security/dist/rule-loader.js
|
|
1523
|
+
var VALID_EFFECTS, VALID_SCOPES, VALID_RISK_LEVELS, RuleLoader;
|
|
1524
|
+
var init_rule_loader = __esm({
|
|
1525
|
+
"../security/dist/rule-loader.js"() {
|
|
1526
|
+
"use strict";
|
|
1527
|
+
VALID_EFFECTS = ["allow", "deny"];
|
|
1528
|
+
VALID_SCOPES = ["global", "user", "conversation", "platform"];
|
|
1529
|
+
VALID_RISK_LEVELS = ["read", "write", "destructive", "admin"];
|
|
1530
|
+
RuleLoader = class {
|
|
1531
|
+
/**
|
|
1532
|
+
* Validates and returns a typed array of SecurityRule objects from a
|
|
1533
|
+
* pre-parsed data object. The config/bootstrap layer is responsible for
|
|
1534
|
+
* YAML parsing; this method only validates structure.
|
|
1535
|
+
*/
|
|
1536
|
+
loadFromObject(data) {
|
|
1537
|
+
if (!data || !Array.isArray(data.rules)) {
|
|
1538
|
+
throw new Error('Invalid data: expected an object with a "rules" array');
|
|
1539
|
+
}
|
|
1540
|
+
return data.rules.map((raw, index) => this.validateRule(raw, index));
|
|
1541
|
+
}
|
|
1542
|
+
validateRule(raw, index) {
|
|
1543
|
+
if (typeof raw !== "object" || raw === null) {
|
|
1544
|
+
throw new Error(`Rule at index ${index} is not an object`);
|
|
1545
|
+
}
|
|
1546
|
+
const rule = raw;
|
|
1547
|
+
if (typeof rule.id !== "string" || rule.id.length === 0) {
|
|
1548
|
+
throw new Error(`Rule at index ${index} is missing a valid "id" string`);
|
|
1549
|
+
}
|
|
1550
|
+
if (typeof rule.effect !== "string" || !VALID_EFFECTS.includes(rule.effect)) {
|
|
1551
|
+
throw new Error(`Rule "${rule.id}" has invalid "effect": expected one of ${VALID_EFFECTS.join(", ")}`);
|
|
1552
|
+
}
|
|
1553
|
+
if (typeof rule.priority !== "number" || !Number.isFinite(rule.priority)) {
|
|
1554
|
+
throw new Error(`Rule "${rule.id}" is missing a valid "priority" number`);
|
|
1555
|
+
}
|
|
1556
|
+
if (typeof rule.scope !== "string" || !VALID_SCOPES.includes(rule.scope)) {
|
|
1557
|
+
throw new Error(`Rule "${rule.id}" has invalid "scope": expected one of ${VALID_SCOPES.join(", ")}`);
|
|
1558
|
+
}
|
|
1559
|
+
if (!Array.isArray(rule.actions) || rule.actions.length === 0) {
|
|
1560
|
+
throw new Error(`Rule "${rule.id}" is missing a valid "actions" array`);
|
|
1561
|
+
}
|
|
1562
|
+
for (const action of rule.actions) {
|
|
1563
|
+
if (typeof action !== "string") {
|
|
1564
|
+
throw new Error(`Rule "${rule.id}" has a non-string entry in "actions"`);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
if (!Array.isArray(rule.riskLevels) || rule.riskLevels.length === 0) {
|
|
1568
|
+
throw new Error(`Rule "${rule.id}" is missing a valid "riskLevels" array`);
|
|
1569
|
+
}
|
|
1570
|
+
for (const level of rule.riskLevels) {
|
|
1571
|
+
if (!VALID_RISK_LEVELS.includes(level)) {
|
|
1572
|
+
throw new Error(`Rule "${rule.id}" has invalid risk level "${level}": expected one of ${VALID_RISK_LEVELS.join(", ")}`);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
const validated = {
|
|
1576
|
+
id: rule.id,
|
|
1577
|
+
effect: rule.effect,
|
|
1578
|
+
priority: rule.priority,
|
|
1579
|
+
scope: rule.scope,
|
|
1580
|
+
actions: rule.actions,
|
|
1581
|
+
riskLevels: rule.riskLevels
|
|
1582
|
+
};
|
|
1583
|
+
if (rule.conditions !== void 0) {
|
|
1584
|
+
if (typeof rule.conditions !== "object" || rule.conditions === null) {
|
|
1585
|
+
throw new Error(`Rule "${rule.id}" has invalid "conditions": expected an object`);
|
|
1586
|
+
}
|
|
1587
|
+
validated.conditions = rule.conditions;
|
|
1588
|
+
}
|
|
1589
|
+
if (rule.rateLimit !== void 0) {
|
|
1590
|
+
if (typeof rule.rateLimit !== "object" || rule.rateLimit === null) {
|
|
1591
|
+
throw new Error(`Rule "${rule.id}" has invalid "rateLimit": expected an object`);
|
|
1592
|
+
}
|
|
1593
|
+
const rl = rule.rateLimit;
|
|
1594
|
+
if (typeof rl.maxInvocations !== "number" || typeof rl.windowSeconds !== "number") {
|
|
1595
|
+
throw new Error(`Rule "${rule.id}" has invalid "rateLimit": expected maxInvocations and windowSeconds numbers`);
|
|
1596
|
+
}
|
|
1597
|
+
validated.rateLimit = rule.rateLimit;
|
|
1598
|
+
}
|
|
1599
|
+
return validated;
|
|
1600
|
+
}
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
// ../security/dist/security-manager.js
|
|
1606
|
+
import crypto3 from "node:crypto";
|
|
1607
|
+
var SecurityManager;
|
|
1608
|
+
var init_security_manager = __esm({
|
|
1609
|
+
"../security/dist/security-manager.js"() {
|
|
1610
|
+
"use strict";
|
|
1611
|
+
SecurityManager = class {
|
|
1612
|
+
ruleEngine;
|
|
1613
|
+
auditRepository;
|
|
1614
|
+
logger;
|
|
1615
|
+
constructor(ruleEngine, auditRepository, logger) {
|
|
1616
|
+
this.ruleEngine = ruleEngine;
|
|
1617
|
+
this.auditRepository = auditRepository;
|
|
1618
|
+
this.logger = logger;
|
|
1619
|
+
}
|
|
1620
|
+
evaluate(context) {
|
|
1621
|
+
const evaluation = this.ruleEngine.evaluate(context);
|
|
1622
|
+
const auditEntry = {
|
|
1623
|
+
id: crypto3.randomUUID(),
|
|
1624
|
+
timestamp: evaluation.timestamp,
|
|
1625
|
+
userId: context.userId,
|
|
1626
|
+
action: context.action,
|
|
1627
|
+
riskLevel: context.riskLevel,
|
|
1628
|
+
ruleId: evaluation.matchedRule?.id,
|
|
1629
|
+
effect: evaluation.allowed ? "allow" : "deny",
|
|
1630
|
+
platform: context.platform,
|
|
1631
|
+
chatId: context.chatId,
|
|
1632
|
+
context: {
|
|
1633
|
+
chatType: context.chatType,
|
|
1634
|
+
reason: evaluation.reason
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
try {
|
|
1638
|
+
this.auditRepository.log(auditEntry);
|
|
1639
|
+
} catch (err) {
|
|
1640
|
+
this.logger.error({ err, auditEntry }, "Failed to write audit log entry");
|
|
1641
|
+
}
|
|
1642
|
+
this.logger.debug({
|
|
1643
|
+
userId: context.userId,
|
|
1644
|
+
action: context.action,
|
|
1645
|
+
allowed: evaluation.allowed,
|
|
1646
|
+
ruleId: evaluation.matchedRule?.id,
|
|
1647
|
+
reason: evaluation.reason
|
|
1648
|
+
}, "Security evaluation completed");
|
|
1649
|
+
return evaluation;
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
// ../security/dist/index.js
|
|
1656
|
+
var init_dist5 = __esm({
|
|
1657
|
+
"../security/dist/index.js"() {
|
|
1658
|
+
"use strict";
|
|
1659
|
+
init_rule_engine();
|
|
1660
|
+
init_rate_limiter();
|
|
1661
|
+
init_rule_loader();
|
|
1662
|
+
init_security_manager();
|
|
1663
|
+
}
|
|
1664
|
+
});
|
|
1665
|
+
|
|
1666
|
+
// ../skills/dist/skill.js
|
|
1667
|
+
var Skill;
|
|
1668
|
+
var init_skill = __esm({
|
|
1669
|
+
"../skills/dist/skill.js"() {
|
|
1670
|
+
"use strict";
|
|
1671
|
+
Skill = class {
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
// ../skills/dist/skill-registry.js
|
|
1677
|
+
var SkillRegistry;
|
|
1678
|
+
var init_skill_registry = __esm({
|
|
1679
|
+
"../skills/dist/skill-registry.js"() {
|
|
1680
|
+
"use strict";
|
|
1681
|
+
SkillRegistry = class {
|
|
1682
|
+
skills = /* @__PURE__ */ new Map();
|
|
1683
|
+
register(skill) {
|
|
1684
|
+
const { name } = skill.metadata;
|
|
1685
|
+
if (this.skills.has(name)) {
|
|
1686
|
+
throw new Error(`Skill "${name}" is already registered`);
|
|
1687
|
+
}
|
|
1688
|
+
this.skills.set(name, skill);
|
|
1689
|
+
}
|
|
1690
|
+
get(name) {
|
|
1691
|
+
return this.skills.get(name);
|
|
1692
|
+
}
|
|
1693
|
+
getAll() {
|
|
1694
|
+
return [...this.skills.values()];
|
|
1695
|
+
}
|
|
1696
|
+
has(name) {
|
|
1697
|
+
return this.skills.has(name);
|
|
1698
|
+
}
|
|
1699
|
+
toToolDefinitions() {
|
|
1700
|
+
return this.getAll().map((skill) => ({
|
|
1701
|
+
name: skill.metadata.name,
|
|
1702
|
+
description: skill.metadata.description,
|
|
1703
|
+
inputSchema: skill.metadata.inputSchema
|
|
1704
|
+
}));
|
|
1705
|
+
}
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
});
|
|
1709
|
+
|
|
1710
|
+
// ../skills/dist/skill-sandbox.js
|
|
1711
|
+
var DEFAULT_TIMEOUT_MS, SkillSandbox;
|
|
1712
|
+
var init_skill_sandbox = __esm({
|
|
1713
|
+
"../skills/dist/skill-sandbox.js"() {
|
|
1714
|
+
"use strict";
|
|
1715
|
+
DEFAULT_TIMEOUT_MS = 3e4;
|
|
1716
|
+
SkillSandbox = class {
|
|
1717
|
+
logger;
|
|
1718
|
+
constructor(logger) {
|
|
1719
|
+
this.logger = logger;
|
|
1720
|
+
}
|
|
1721
|
+
async execute(skill, input2, context, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
1722
|
+
const { name } = skill.metadata;
|
|
1723
|
+
this.logger.info({ skill: name, input: input2 }, "Skill execution started");
|
|
1724
|
+
try {
|
|
1725
|
+
const result = await Promise.race([
|
|
1726
|
+
skill.execute(input2, context),
|
|
1727
|
+
new Promise((_resolve, reject) => {
|
|
1728
|
+
setTimeout(() => reject(new Error(`Skill "${name}" timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
1729
|
+
})
|
|
1730
|
+
]);
|
|
1731
|
+
this.logger.info({ skill: name, success: result.success }, "Skill execution completed");
|
|
1732
|
+
return result;
|
|
1733
|
+
} catch (error) {
|
|
1734
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1735
|
+
this.logger.error({ skill: name, error: message }, "Skill execution failed");
|
|
1736
|
+
return {
|
|
1737
|
+
success: false,
|
|
1738
|
+
error: message
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
|
|
1746
|
+
// ../skills/dist/plugin-loader.js
|
|
1747
|
+
import fs3 from "node:fs";
|
|
1748
|
+
import path3 from "node:path";
|
|
1749
|
+
var init_plugin_loader = __esm({
|
|
1750
|
+
"../skills/dist/plugin-loader.js"() {
|
|
1751
|
+
"use strict";
|
|
1752
|
+
init_skill();
|
|
1753
|
+
}
|
|
1754
|
+
});
|
|
1755
|
+
|
|
1756
|
+
// ../skills/dist/built-in/calculator.js
|
|
1757
|
+
var ALLOWED_PATTERN, SAFE_EXPRESSION_PATTERN, CalculatorSkill;
|
|
1758
|
+
var init_calculator = __esm({
|
|
1759
|
+
"../skills/dist/built-in/calculator.js"() {
|
|
1760
|
+
"use strict";
|
|
1761
|
+
init_skill();
|
|
1762
|
+
ALLOWED_PATTERN = /^[\d+\-*/().,%\s]|Math\.(sin|cos|tan|sqrt|pow|abs|floor|ceil|round|log|log2|log10|PI|E)/;
|
|
1763
|
+
SAFE_EXPRESSION_PATTERN = /^[0-9+\-*/().,\s%]*(Math\.(sin|cos|tan|sqrt|pow|abs|floor|ceil|round|log|log2|log10|PI|E)[(0-9+\-*/().,\s%]*)*$/;
|
|
1764
|
+
CalculatorSkill = class extends Skill {
|
|
1765
|
+
metadata = {
|
|
1766
|
+
name: "calculator",
|
|
1767
|
+
description: "Evaluate mathematical expressions safely",
|
|
1768
|
+
riskLevel: "read",
|
|
1769
|
+
version: "1.0.0",
|
|
1770
|
+
inputSchema: {
|
|
1771
|
+
type: "object",
|
|
1772
|
+
properties: {
|
|
1773
|
+
expression: {
|
|
1774
|
+
type: "string",
|
|
1775
|
+
description: "The mathematical expression to evaluate"
|
|
1776
|
+
}
|
|
1777
|
+
},
|
|
1778
|
+
required: ["expression"]
|
|
1779
|
+
}
|
|
1780
|
+
};
|
|
1781
|
+
async execute(input2, _context) {
|
|
1782
|
+
const expression = input2.expression;
|
|
1783
|
+
if (!expression || typeof expression !== "string") {
|
|
1784
|
+
return {
|
|
1785
|
+
success: false,
|
|
1786
|
+
error: "Invalid expression: input must be a non-empty string"
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
const trimmed = expression.trim();
|
|
1790
|
+
if (!ALLOWED_PATTERN.test(trimmed)) {
|
|
1791
|
+
return {
|
|
1792
|
+
success: false,
|
|
1793
|
+
error: `Invalid expression: "${trimmed}" contains disallowed characters`
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
if (!SAFE_EXPRESSION_PATTERN.test(trimmed)) {
|
|
1797
|
+
return {
|
|
1798
|
+
success: false,
|
|
1799
|
+
error: `Invalid expression: "${trimmed}" contains disallowed constructs`
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
try {
|
|
1803
|
+
const fn = new Function("Math", `"use strict"; return (${trimmed});`);
|
|
1804
|
+
const result = fn(Math);
|
|
1805
|
+
if (typeof result !== "number" || !isFinite(result)) {
|
|
1806
|
+
return {
|
|
1807
|
+
success: false,
|
|
1808
|
+
error: `Invalid expression: "${trimmed}" did not produce a finite number`
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
return {
|
|
1812
|
+
success: true,
|
|
1813
|
+
data: result,
|
|
1814
|
+
display: `${trimmed} = ${result}`
|
|
1815
|
+
};
|
|
1816
|
+
} catch {
|
|
1817
|
+
return {
|
|
1818
|
+
success: false,
|
|
1819
|
+
error: `Invalid expression: "${trimmed}"`
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
});
|
|
1826
|
+
|
|
1827
|
+
// ../skills/dist/built-in/system-info.js
|
|
1828
|
+
var SystemInfoSkill;
|
|
1829
|
+
var init_system_info = __esm({
|
|
1830
|
+
"../skills/dist/built-in/system-info.js"() {
|
|
1831
|
+
"use strict";
|
|
1832
|
+
init_skill();
|
|
1833
|
+
SystemInfoSkill = class extends Skill {
|
|
1834
|
+
metadata = {
|
|
1835
|
+
name: "system_info",
|
|
1836
|
+
description: "Get system information about the Alfred bot",
|
|
1837
|
+
riskLevel: "read",
|
|
1838
|
+
version: "1.0.0",
|
|
1839
|
+
inputSchema: {
|
|
1840
|
+
type: "object",
|
|
1841
|
+
properties: {
|
|
1842
|
+
category: {
|
|
1843
|
+
type: "string",
|
|
1844
|
+
enum: ["general", "memory", "uptime"],
|
|
1845
|
+
description: "Category of system info"
|
|
1846
|
+
}
|
|
1847
|
+
},
|
|
1848
|
+
required: ["category"]
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
async execute(input2, _context) {
|
|
1852
|
+
const category = input2.category;
|
|
1853
|
+
switch (category) {
|
|
1854
|
+
case "general":
|
|
1855
|
+
return this.getGeneralInfo();
|
|
1856
|
+
case "memory":
|
|
1857
|
+
return this.getMemoryInfo();
|
|
1858
|
+
case "uptime":
|
|
1859
|
+
return this.getUptimeInfo();
|
|
1860
|
+
default:
|
|
1861
|
+
return {
|
|
1862
|
+
success: false,
|
|
1863
|
+
error: `Unknown category: "${String(category)}". Valid categories: general, memory, uptime`
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
getGeneralInfo() {
|
|
1868
|
+
const info = {
|
|
1869
|
+
nodeVersion: process.version,
|
|
1870
|
+
platform: process.platform,
|
|
1871
|
+
arch: process.arch
|
|
1872
|
+
};
|
|
1873
|
+
return {
|
|
1874
|
+
success: true,
|
|
1875
|
+
data: info,
|
|
1876
|
+
display: `Node.js ${info.nodeVersion} on ${info.platform} (${info.arch})`
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
getMemoryInfo() {
|
|
1880
|
+
const mem = process.memoryUsage();
|
|
1881
|
+
const toMB = (bytes) => (bytes / 1024 / 1024).toFixed(2);
|
|
1882
|
+
const info = {
|
|
1883
|
+
rss: `${toMB(mem.rss)} MB`,
|
|
1884
|
+
heapTotal: `${toMB(mem.heapTotal)} MB`,
|
|
1885
|
+
heapUsed: `${toMB(mem.heapUsed)} MB`,
|
|
1886
|
+
external: `${toMB(mem.external)} MB`
|
|
1887
|
+
};
|
|
1888
|
+
return {
|
|
1889
|
+
success: true,
|
|
1890
|
+
data: info,
|
|
1891
|
+
display: `Memory \u2014 RSS: ${info.rss}, Heap: ${info.heapUsed} / ${info.heapTotal}, External: ${info.external}`
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
getUptimeInfo() {
|
|
1895
|
+
const totalSeconds = process.uptime();
|
|
1896
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
1897
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
1898
|
+
const seconds = Math.floor(totalSeconds % 60);
|
|
1899
|
+
const info = {
|
|
1900
|
+
uptimeSeconds: totalSeconds,
|
|
1901
|
+
formatted: `${hours}h ${minutes}m ${seconds}s`
|
|
1902
|
+
};
|
|
1903
|
+
return {
|
|
1904
|
+
success: true,
|
|
1905
|
+
data: info,
|
|
1906
|
+
display: `Uptime: ${info.formatted}`
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
});
|
|
1912
|
+
|
|
1913
|
+
// ../skills/dist/built-in/web-search.js
|
|
1914
|
+
var WebSearchSkill;
|
|
1915
|
+
var init_web_search = __esm({
|
|
1916
|
+
"../skills/dist/built-in/web-search.js"() {
|
|
1917
|
+
"use strict";
|
|
1918
|
+
init_skill();
|
|
1919
|
+
WebSearchSkill = class extends Skill {
|
|
1920
|
+
metadata = {
|
|
1921
|
+
name: "web_search",
|
|
1922
|
+
description: "Search the web (placeholder \u2014 returns mock results)",
|
|
1923
|
+
riskLevel: "read",
|
|
1924
|
+
version: "0.1.0",
|
|
1925
|
+
inputSchema: {
|
|
1926
|
+
type: "object",
|
|
1927
|
+
properties: {
|
|
1928
|
+
query: {
|
|
1929
|
+
type: "string",
|
|
1930
|
+
description: "Search query"
|
|
1931
|
+
}
|
|
1932
|
+
},
|
|
1933
|
+
required: ["query"]
|
|
1934
|
+
}
|
|
1935
|
+
};
|
|
1936
|
+
async execute(input2, _context) {
|
|
1937
|
+
const query = input2.query;
|
|
1938
|
+
return {
|
|
1939
|
+
success: true,
|
|
1940
|
+
data: {
|
|
1941
|
+
note: "Web search is not yet connected to a search API"
|
|
1942
|
+
},
|
|
1943
|
+
display: `Web search for "${query}" is not yet implemented. This skill will be connected to a search API in a future update.`
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
});
|
|
1949
|
+
|
|
1950
|
+
// ../skills/dist/built-in/reminder.js
|
|
1951
|
+
import { randomUUID } from "node:crypto";
|
|
1952
|
+
var ReminderSkill;
|
|
1953
|
+
var init_reminder = __esm({
|
|
1954
|
+
"../skills/dist/built-in/reminder.js"() {
|
|
1955
|
+
"use strict";
|
|
1956
|
+
init_skill();
|
|
1957
|
+
ReminderSkill = class extends Skill {
|
|
1958
|
+
metadata = {
|
|
1959
|
+
name: "reminder",
|
|
1960
|
+
description: "Set, list, or cancel reminders",
|
|
1961
|
+
riskLevel: "write",
|
|
1962
|
+
version: "1.0.0",
|
|
1963
|
+
inputSchema: {
|
|
1964
|
+
type: "object",
|
|
1965
|
+
properties: {
|
|
1966
|
+
action: {
|
|
1967
|
+
type: "string",
|
|
1968
|
+
enum: ["set", "list", "cancel"],
|
|
1969
|
+
description: "The reminder action to perform"
|
|
1970
|
+
},
|
|
1971
|
+
message: {
|
|
1972
|
+
type: "string",
|
|
1973
|
+
description: "The reminder message (required for set)"
|
|
1974
|
+
},
|
|
1975
|
+
delayMinutes: {
|
|
1976
|
+
type: "number",
|
|
1977
|
+
description: "Minutes until the reminder triggers (required for set)"
|
|
1978
|
+
},
|
|
1979
|
+
reminderId: {
|
|
1980
|
+
type: "string",
|
|
1981
|
+
description: "The ID of the reminder to cancel (required for cancel)"
|
|
1982
|
+
}
|
|
1983
|
+
},
|
|
1984
|
+
required: ["action"]
|
|
1985
|
+
}
|
|
1986
|
+
};
|
|
1987
|
+
reminders = /* @__PURE__ */ new Map();
|
|
1988
|
+
async execute(input2, context) {
|
|
1989
|
+
const action = input2.action;
|
|
1990
|
+
switch (action) {
|
|
1991
|
+
case "set":
|
|
1992
|
+
return this.setReminder(input2, context);
|
|
1993
|
+
case "list":
|
|
1994
|
+
return this.listReminders(context);
|
|
1995
|
+
case "cancel":
|
|
1996
|
+
return this.cancelReminder(input2);
|
|
1997
|
+
default:
|
|
1998
|
+
return {
|
|
1999
|
+
success: false,
|
|
2000
|
+
error: `Unknown action: "${String(action)}". Valid actions: set, list, cancel`
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
setReminder(input2, context) {
|
|
2005
|
+
const message = input2.message;
|
|
2006
|
+
const delayMinutes = input2.delayMinutes;
|
|
2007
|
+
if (!message || typeof message !== "string") {
|
|
2008
|
+
return {
|
|
2009
|
+
success: false,
|
|
2010
|
+
error: 'Missing required field "message" for set action'
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
if (delayMinutes === void 0 || typeof delayMinutes !== "number" || delayMinutes <= 0) {
|
|
2014
|
+
return {
|
|
2015
|
+
success: false,
|
|
2016
|
+
error: 'Missing or invalid "delayMinutes" for set action (must be a positive number)'
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
const reminderId = randomUUID();
|
|
2020
|
+
const triggerAt = Date.now() + delayMinutes * 60 * 1e3;
|
|
2021
|
+
const timeout = setTimeout(() => {
|
|
2022
|
+
this.reminders.delete(reminderId);
|
|
2023
|
+
}, delayMinutes * 60 * 1e3);
|
|
2024
|
+
this.reminders.set(reminderId, {
|
|
2025
|
+
userId: context.userId,
|
|
2026
|
+
message,
|
|
2027
|
+
triggerAt,
|
|
2028
|
+
timeout
|
|
2029
|
+
});
|
|
2030
|
+
return {
|
|
2031
|
+
success: true,
|
|
2032
|
+
data: { reminderId, message, triggerAt },
|
|
2033
|
+
display: `Reminder set (${reminderId}): "${message}" in ${delayMinutes} minute(s)`
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
listReminders(context) {
|
|
2037
|
+
const userReminders = [];
|
|
2038
|
+
for (const [reminderId, entry] of this.reminders) {
|
|
2039
|
+
if (entry.userId === context.userId) {
|
|
2040
|
+
userReminders.push({
|
|
2041
|
+
reminderId,
|
|
2042
|
+
message: entry.message,
|
|
2043
|
+
triggerAt: entry.triggerAt
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
return {
|
|
2048
|
+
success: true,
|
|
2049
|
+
data: userReminders,
|
|
2050
|
+
display: userReminders.length === 0 ? "No active reminders." : `Active reminders:
|
|
2051
|
+
${userReminders.map((r) => `- ${r.reminderId}: "${r.message}" (triggers at ${new Date(r.triggerAt).toISOString()})`).join("\n")}`
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
cancelReminder(input2) {
|
|
2055
|
+
const reminderId = input2.reminderId;
|
|
2056
|
+
if (!reminderId || typeof reminderId !== "string") {
|
|
2057
|
+
return {
|
|
2058
|
+
success: false,
|
|
2059
|
+
error: 'Missing required field "reminderId" for cancel action'
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
const entry = this.reminders.get(reminderId);
|
|
2063
|
+
if (!entry) {
|
|
2064
|
+
return {
|
|
2065
|
+
success: false,
|
|
2066
|
+
error: `Reminder "${reminderId}" not found`
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
clearTimeout(entry.timeout);
|
|
2070
|
+
this.reminders.delete(reminderId);
|
|
2071
|
+
return {
|
|
2072
|
+
success: true,
|
|
2073
|
+
data: { reminderId },
|
|
2074
|
+
display: `Reminder "${reminderId}" cancelled.`
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
});
|
|
2080
|
+
|
|
2081
|
+
// ../skills/dist/built-in/note.js
|
|
2082
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
2083
|
+
var NoteSkill;
|
|
2084
|
+
var init_note = __esm({
|
|
2085
|
+
"../skills/dist/built-in/note.js"() {
|
|
2086
|
+
"use strict";
|
|
2087
|
+
init_skill();
|
|
2088
|
+
NoteSkill = class extends Skill {
|
|
2089
|
+
metadata = {
|
|
2090
|
+
name: "note",
|
|
2091
|
+
description: "Save, list, search, or delete notes",
|
|
2092
|
+
riskLevel: "write",
|
|
2093
|
+
version: "1.0.0",
|
|
2094
|
+
inputSchema: {
|
|
2095
|
+
type: "object",
|
|
2096
|
+
properties: {
|
|
2097
|
+
action: {
|
|
2098
|
+
type: "string",
|
|
2099
|
+
enum: ["save", "list", "search", "delete"],
|
|
2100
|
+
description: "The note action to perform"
|
|
2101
|
+
},
|
|
2102
|
+
title: {
|
|
2103
|
+
type: "string",
|
|
2104
|
+
description: "The note title (required for save)"
|
|
2105
|
+
},
|
|
2106
|
+
content: {
|
|
2107
|
+
type: "string",
|
|
2108
|
+
description: "The note content (required for save)"
|
|
2109
|
+
},
|
|
2110
|
+
noteId: {
|
|
2111
|
+
type: "string",
|
|
2112
|
+
description: "The ID of the note to delete (required for delete)"
|
|
2113
|
+
},
|
|
2114
|
+
query: {
|
|
2115
|
+
type: "string",
|
|
2116
|
+
description: "Search query to filter notes (required for search)"
|
|
2117
|
+
}
|
|
2118
|
+
},
|
|
2119
|
+
required: ["action"]
|
|
2120
|
+
}
|
|
2121
|
+
};
|
|
2122
|
+
notes = /* @__PURE__ */ new Map();
|
|
2123
|
+
async execute(input2, context) {
|
|
2124
|
+
const action = input2.action;
|
|
2125
|
+
switch (action) {
|
|
2126
|
+
case "save":
|
|
2127
|
+
return this.saveNote(input2, context);
|
|
2128
|
+
case "list":
|
|
2129
|
+
return this.listNotes(context);
|
|
2130
|
+
case "search":
|
|
2131
|
+
return this.searchNotes(input2, context);
|
|
2132
|
+
case "delete":
|
|
2133
|
+
return this.deleteNote(input2);
|
|
2134
|
+
default:
|
|
2135
|
+
return {
|
|
2136
|
+
success: false,
|
|
2137
|
+
error: `Unknown action: "${String(action)}". Valid actions: save, list, search, delete`
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
saveNote(input2, context) {
|
|
2142
|
+
const title = input2.title;
|
|
2143
|
+
const content = input2.content;
|
|
2144
|
+
if (!title || typeof title !== "string") {
|
|
2145
|
+
return {
|
|
2146
|
+
success: false,
|
|
2147
|
+
error: 'Missing required field "title" for save action'
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
if (!content || typeof content !== "string") {
|
|
2151
|
+
return {
|
|
2152
|
+
success: false,
|
|
2153
|
+
error: 'Missing required field "content" for save action'
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
const noteId = randomUUID2();
|
|
2157
|
+
const createdAt = Date.now();
|
|
2158
|
+
this.notes.set(noteId, {
|
|
2159
|
+
noteId,
|
|
2160
|
+
userId: context.userId,
|
|
2161
|
+
title,
|
|
2162
|
+
content,
|
|
2163
|
+
createdAt
|
|
2164
|
+
});
|
|
2165
|
+
return {
|
|
2166
|
+
success: true,
|
|
2167
|
+
data: { noteId, title, createdAt },
|
|
2168
|
+
display: `Note saved (${noteId}): "${title}"`
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
listNotes(context) {
|
|
2172
|
+
const userNotes = [];
|
|
2173
|
+
for (const [, entry] of this.notes) {
|
|
2174
|
+
if (entry.userId === context.userId) {
|
|
2175
|
+
userNotes.push({
|
|
2176
|
+
noteId: entry.noteId,
|
|
2177
|
+
title: entry.title,
|
|
2178
|
+
createdAt: entry.createdAt
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
return {
|
|
2183
|
+
success: true,
|
|
2184
|
+
data: userNotes,
|
|
2185
|
+
display: userNotes.length === 0 ? "No notes found." : `Notes:
|
|
2186
|
+
${userNotes.map((n) => `- ${n.noteId}: "${n.title}"`).join("\n")}`
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
searchNotes(input2, context) {
|
|
2190
|
+
const query = input2.query;
|
|
2191
|
+
if (!query || typeof query !== "string") {
|
|
2192
|
+
return {
|
|
2193
|
+
success: false,
|
|
2194
|
+
error: 'Missing required field "query" for search action'
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
const lowerQuery = query.toLowerCase();
|
|
2198
|
+
const matches = [];
|
|
2199
|
+
for (const [, entry] of this.notes) {
|
|
2200
|
+
if (entry.userId !== context.userId) {
|
|
2201
|
+
continue;
|
|
2202
|
+
}
|
|
2203
|
+
if (entry.title.toLowerCase().includes(lowerQuery) || entry.content.toLowerCase().includes(lowerQuery)) {
|
|
2204
|
+
matches.push({
|
|
2205
|
+
noteId: entry.noteId,
|
|
2206
|
+
title: entry.title,
|
|
2207
|
+
content: entry.content
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
return {
|
|
2212
|
+
success: true,
|
|
2213
|
+
data: matches,
|
|
2214
|
+
display: matches.length === 0 ? `No notes matching "${query}".` : `Found ${matches.length} note(s):
|
|
2215
|
+
${matches.map((n) => `- ${n.noteId}: "${n.title}"`).join("\n")}`
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
deleteNote(input2) {
|
|
2219
|
+
const noteId = input2.noteId;
|
|
2220
|
+
if (!noteId || typeof noteId !== "string") {
|
|
2221
|
+
return {
|
|
2222
|
+
success: false,
|
|
2223
|
+
error: 'Missing required field "noteId" for delete action'
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2226
|
+
const entry = this.notes.get(noteId);
|
|
2227
|
+
if (!entry) {
|
|
2228
|
+
return {
|
|
2229
|
+
success: false,
|
|
2230
|
+
error: `Note "${noteId}" not found`
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
this.notes.delete(noteId);
|
|
2234
|
+
return {
|
|
2235
|
+
success: true,
|
|
2236
|
+
data: { noteId },
|
|
2237
|
+
display: `Note "${noteId}" deleted.`
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
});
|
|
2243
|
+
|
|
2244
|
+
// ../skills/dist/built-in/summarize.js
|
|
2245
|
+
var DEFAULT_MAX_LENGTH, SummarizeSkill;
|
|
2246
|
+
var init_summarize = __esm({
|
|
2247
|
+
"../skills/dist/built-in/summarize.js"() {
|
|
2248
|
+
"use strict";
|
|
2249
|
+
init_skill();
|
|
2250
|
+
DEFAULT_MAX_LENGTH = 280;
|
|
2251
|
+
SummarizeSkill = class extends Skill {
|
|
2252
|
+
metadata = {
|
|
2253
|
+
name: "summarize",
|
|
2254
|
+
description: "Produce an extractive summary of the given text",
|
|
2255
|
+
riskLevel: "read",
|
|
2256
|
+
version: "1.0.0",
|
|
2257
|
+
inputSchema: {
|
|
2258
|
+
type: "object",
|
|
2259
|
+
properties: {
|
|
2260
|
+
text: {
|
|
2261
|
+
type: "string",
|
|
2262
|
+
description: "The text to summarize"
|
|
2263
|
+
},
|
|
2264
|
+
maxLength: {
|
|
2265
|
+
type: "number",
|
|
2266
|
+
description: "Maximum character length for the summary (default: 280)"
|
|
2267
|
+
}
|
|
2268
|
+
},
|
|
2269
|
+
required: ["text"]
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
async execute(input2, _context) {
|
|
2273
|
+
const text = input2.text;
|
|
2274
|
+
const maxLength = input2.maxLength ?? DEFAULT_MAX_LENGTH;
|
|
2275
|
+
if (!text || typeof text !== "string") {
|
|
2276
|
+
return {
|
|
2277
|
+
success: false,
|
|
2278
|
+
error: 'Invalid input: "text" must be a non-empty string'
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
if (text.length <= maxLength) {
|
|
2282
|
+
return {
|
|
2283
|
+
success: true,
|
|
2284
|
+
data: { summary: text },
|
|
2285
|
+
display: text
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
const summary = this.extractiveSummarize(text, maxLength);
|
|
2289
|
+
return {
|
|
2290
|
+
success: true,
|
|
2291
|
+
data: { summary },
|
|
2292
|
+
display: summary
|
|
2293
|
+
};
|
|
2294
|
+
}
|
|
2295
|
+
extractiveSummarize(text, maxLength) {
|
|
2296
|
+
const sentences = text.split(/(?<=[.!?])\s+/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
2297
|
+
if (sentences.length === 0) {
|
|
2298
|
+
return text.slice(0, maxLength);
|
|
2299
|
+
}
|
|
2300
|
+
const wordFrequency = this.buildWordFrequency(text);
|
|
2301
|
+
const scored = sentences.map((sentence, index) => ({
|
|
2302
|
+
sentence,
|
|
2303
|
+
index,
|
|
2304
|
+
score: this.scoreSentence(sentence, wordFrequency)
|
|
2305
|
+
}));
|
|
2306
|
+
const ranked = [...scored].sort((a, b) => b.score - a.score);
|
|
2307
|
+
const selected = [];
|
|
2308
|
+
let currentLength = 0;
|
|
2309
|
+
for (const entry of ranked) {
|
|
2310
|
+
const addition = currentLength === 0 ? entry.sentence.length : entry.sentence.length + 1;
|
|
2311
|
+
if (currentLength + addition > maxLength) {
|
|
2312
|
+
continue;
|
|
2313
|
+
}
|
|
2314
|
+
selected.push(entry);
|
|
2315
|
+
currentLength += addition;
|
|
2316
|
+
}
|
|
2317
|
+
if (selected.length === 0) {
|
|
2318
|
+
return sentences[0].slice(0, maxLength);
|
|
2319
|
+
}
|
|
2320
|
+
selected.sort((a, b) => a.index - b.index);
|
|
2321
|
+
return selected.map((s) => s.sentence).join(" ");
|
|
2322
|
+
}
|
|
2323
|
+
buildWordFrequency(text) {
|
|
2324
|
+
const stopWords = /* @__PURE__ */ new Set([
|
|
2325
|
+
"the",
|
|
2326
|
+
"a",
|
|
2327
|
+
"an",
|
|
2328
|
+
"is",
|
|
2329
|
+
"are",
|
|
2330
|
+
"was",
|
|
2331
|
+
"were",
|
|
2332
|
+
"be",
|
|
2333
|
+
"been",
|
|
2334
|
+
"being",
|
|
2335
|
+
"have",
|
|
2336
|
+
"has",
|
|
2337
|
+
"had",
|
|
2338
|
+
"do",
|
|
2339
|
+
"does",
|
|
2340
|
+
"did",
|
|
2341
|
+
"will",
|
|
2342
|
+
"would",
|
|
2343
|
+
"could",
|
|
2344
|
+
"should",
|
|
2345
|
+
"may",
|
|
2346
|
+
"might",
|
|
2347
|
+
"shall",
|
|
2348
|
+
"can",
|
|
2349
|
+
"to",
|
|
2350
|
+
"of",
|
|
2351
|
+
"in",
|
|
2352
|
+
"for",
|
|
2353
|
+
"on",
|
|
2354
|
+
"with",
|
|
2355
|
+
"at",
|
|
2356
|
+
"by",
|
|
2357
|
+
"from",
|
|
2358
|
+
"as",
|
|
2359
|
+
"into",
|
|
2360
|
+
"through",
|
|
2361
|
+
"during",
|
|
2362
|
+
"before",
|
|
2363
|
+
"after",
|
|
2364
|
+
"and",
|
|
2365
|
+
"but",
|
|
2366
|
+
"or",
|
|
2367
|
+
"nor",
|
|
2368
|
+
"not",
|
|
2369
|
+
"so",
|
|
2370
|
+
"yet",
|
|
2371
|
+
"both",
|
|
2372
|
+
"either",
|
|
2373
|
+
"neither",
|
|
2374
|
+
"each",
|
|
2375
|
+
"every",
|
|
2376
|
+
"all",
|
|
2377
|
+
"any",
|
|
2378
|
+
"few",
|
|
2379
|
+
"more",
|
|
2380
|
+
"most",
|
|
2381
|
+
"other",
|
|
2382
|
+
"some",
|
|
2383
|
+
"such",
|
|
2384
|
+
"no",
|
|
2385
|
+
"only",
|
|
2386
|
+
"own",
|
|
2387
|
+
"same",
|
|
2388
|
+
"than",
|
|
2389
|
+
"too",
|
|
2390
|
+
"very",
|
|
2391
|
+
"just",
|
|
2392
|
+
"because",
|
|
2393
|
+
"if",
|
|
2394
|
+
"when",
|
|
2395
|
+
"where",
|
|
2396
|
+
"how",
|
|
2397
|
+
"what",
|
|
2398
|
+
"which",
|
|
2399
|
+
"who",
|
|
2400
|
+
"whom",
|
|
2401
|
+
"this",
|
|
2402
|
+
"that",
|
|
2403
|
+
"these",
|
|
2404
|
+
"those",
|
|
2405
|
+
"it",
|
|
2406
|
+
"its",
|
|
2407
|
+
"i",
|
|
2408
|
+
"me",
|
|
2409
|
+
"my",
|
|
2410
|
+
"we",
|
|
2411
|
+
"our",
|
|
2412
|
+
"you",
|
|
2413
|
+
"your",
|
|
2414
|
+
"he",
|
|
2415
|
+
"him",
|
|
2416
|
+
"his",
|
|
2417
|
+
"she",
|
|
2418
|
+
"her",
|
|
2419
|
+
"they",
|
|
2420
|
+
"them",
|
|
2421
|
+
"their"
|
|
2422
|
+
]);
|
|
2423
|
+
const frequency = /* @__PURE__ */ new Map();
|
|
2424
|
+
const words = text.toLowerCase().match(/\b[a-z]+\b/g) ?? [];
|
|
2425
|
+
for (const word of words) {
|
|
2426
|
+
if (stopWords.has(word) || word.length < 3) {
|
|
2427
|
+
continue;
|
|
2428
|
+
}
|
|
2429
|
+
frequency.set(word, (frequency.get(word) ?? 0) + 1);
|
|
2430
|
+
}
|
|
2431
|
+
return frequency;
|
|
2432
|
+
}
|
|
2433
|
+
scoreSentence(sentence, wordFrequency) {
|
|
2434
|
+
const words = sentence.toLowerCase().match(/\b[a-z]+\b/g) ?? [];
|
|
2435
|
+
let score = 0;
|
|
2436
|
+
for (const word of words) {
|
|
2437
|
+
score += wordFrequency.get(word) ?? 0;
|
|
2438
|
+
}
|
|
2439
|
+
return score;
|
|
2440
|
+
}
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
});
|
|
2444
|
+
|
|
2445
|
+
// ../skills/dist/built-in/translate.js
|
|
2446
|
+
var TranslateSkill;
|
|
2447
|
+
var init_translate = __esm({
|
|
2448
|
+
"../skills/dist/built-in/translate.js"() {
|
|
2449
|
+
"use strict";
|
|
2450
|
+
init_skill();
|
|
2451
|
+
TranslateSkill = class extends Skill {
|
|
2452
|
+
metadata = {
|
|
2453
|
+
name: "translate",
|
|
2454
|
+
description: "Translate text between languages (placeholder \u2014 requires external API)",
|
|
2455
|
+
riskLevel: "read",
|
|
2456
|
+
version: "0.1.0",
|
|
2457
|
+
inputSchema: {
|
|
2458
|
+
type: "object",
|
|
2459
|
+
properties: {
|
|
2460
|
+
text: {
|
|
2461
|
+
type: "string",
|
|
2462
|
+
description: "The text to translate"
|
|
2463
|
+
},
|
|
2464
|
+
targetLanguage: {
|
|
2465
|
+
type: "string",
|
|
2466
|
+
description: 'The language to translate into (e.g. "es", "fr", "de")'
|
|
2467
|
+
},
|
|
2468
|
+
sourceLanguage: {
|
|
2469
|
+
type: "string",
|
|
2470
|
+
description: "The source language (optional, auto-detected if omitted)"
|
|
2471
|
+
}
|
|
2472
|
+
},
|
|
2473
|
+
required: ["text", "targetLanguage"]
|
|
2474
|
+
}
|
|
2475
|
+
};
|
|
2476
|
+
async execute(input2, _context) {
|
|
2477
|
+
const text = input2.text;
|
|
2478
|
+
const targetLanguage = input2.targetLanguage;
|
|
2479
|
+
const sourceLanguage = input2.sourceLanguage;
|
|
2480
|
+
if (!text || typeof text !== "string") {
|
|
2481
|
+
return {
|
|
2482
|
+
success: false,
|
|
2483
|
+
error: 'Invalid input: "text" must be a non-empty string'
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
if (!targetLanguage || typeof targetLanguage !== "string") {
|
|
2487
|
+
return {
|
|
2488
|
+
success: false,
|
|
2489
|
+
error: 'Invalid input: "targetLanguage" must be a non-empty string'
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
const sourceLabel = sourceLanguage ? ` from "${sourceLanguage}"` : "";
|
|
2493
|
+
return {
|
|
2494
|
+
success: true,
|
|
2495
|
+
data: {
|
|
2496
|
+
note: "Translation is not yet connected to a translation API",
|
|
2497
|
+
text,
|
|
2498
|
+
targetLanguage,
|
|
2499
|
+
sourceLanguage: sourceLanguage ?? "auto"
|
|
2500
|
+
},
|
|
2501
|
+
display: `Translation${sourceLabel} to "${targetLanguage}" is not yet implemented. This skill will be connected to a translation API in a future update.
|
|
2502
|
+
|
|
2503
|
+
Requested text: "${text}"`
|
|
2504
|
+
};
|
|
2505
|
+
}
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
});
|
|
2509
|
+
|
|
2510
|
+
// ../skills/dist/built-in/weather.js
|
|
2511
|
+
var WeatherSkill;
|
|
2512
|
+
var init_weather = __esm({
|
|
2513
|
+
"../skills/dist/built-in/weather.js"() {
|
|
2514
|
+
"use strict";
|
|
2515
|
+
init_skill();
|
|
2516
|
+
WeatherSkill = class extends Skill {
|
|
2517
|
+
metadata = {
|
|
2518
|
+
name: "weather",
|
|
2519
|
+
description: "Get weather information for a location (placeholder \u2014 requires API key)",
|
|
2520
|
+
riskLevel: "read",
|
|
2521
|
+
version: "0.1.0",
|
|
2522
|
+
inputSchema: {
|
|
2523
|
+
type: "object",
|
|
2524
|
+
properties: {
|
|
2525
|
+
location: {
|
|
2526
|
+
type: "string",
|
|
2527
|
+
description: 'The location to get weather for (e.g. "London", "New York, NY")'
|
|
2528
|
+
},
|
|
2529
|
+
units: {
|
|
2530
|
+
type: "string",
|
|
2531
|
+
enum: ["metric", "imperial"],
|
|
2532
|
+
description: "Unit system for temperature (default: metric)"
|
|
2533
|
+
}
|
|
2534
|
+
},
|
|
2535
|
+
required: ["location"]
|
|
2536
|
+
}
|
|
2537
|
+
};
|
|
2538
|
+
async execute(input2, _context) {
|
|
2539
|
+
const location = input2.location;
|
|
2540
|
+
const units = input2.units ?? "metric";
|
|
2541
|
+
if (!location || typeof location !== "string") {
|
|
2542
|
+
return {
|
|
2543
|
+
success: false,
|
|
2544
|
+
error: 'Invalid input: "location" must be a non-empty string'
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
return {
|
|
2548
|
+
success: true,
|
|
2549
|
+
data: {
|
|
2550
|
+
note: "Weather data is not yet available \u2014 API key configuration required",
|
|
2551
|
+
location,
|
|
2552
|
+
units
|
|
2553
|
+
},
|
|
2554
|
+
display: `Weather for "${location}" (${units}) is not yet implemented. This skill requires a weather API key to be configured.`
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
};
|
|
2558
|
+
}
|
|
2559
|
+
});
|
|
2560
|
+
|
|
2561
|
+
// ../skills/dist/index.js
|
|
2562
|
+
var init_dist6 = __esm({
|
|
2563
|
+
"../skills/dist/index.js"() {
|
|
2564
|
+
"use strict";
|
|
2565
|
+
init_skill();
|
|
2566
|
+
init_skill_registry();
|
|
2567
|
+
init_skill_sandbox();
|
|
2568
|
+
init_plugin_loader();
|
|
2569
|
+
init_calculator();
|
|
2570
|
+
init_system_info();
|
|
2571
|
+
init_web_search();
|
|
2572
|
+
init_reminder();
|
|
2573
|
+
init_note();
|
|
2574
|
+
init_summarize();
|
|
2575
|
+
init_translate();
|
|
2576
|
+
init_weather();
|
|
2577
|
+
}
|
|
2578
|
+
});
|
|
2579
|
+
|
|
2580
|
+
// ../core/dist/conversation-manager.js
|
|
2581
|
+
var ConversationManager;
|
|
2582
|
+
var init_conversation_manager = __esm({
|
|
2583
|
+
"../core/dist/conversation-manager.js"() {
|
|
2584
|
+
"use strict";
|
|
2585
|
+
ConversationManager = class {
|
|
2586
|
+
conversations;
|
|
2587
|
+
constructor(conversations) {
|
|
2588
|
+
this.conversations = conversations;
|
|
2589
|
+
}
|
|
2590
|
+
getOrCreateConversation(platform, chatId, userId) {
|
|
2591
|
+
const existing = this.conversations.findByPlatformChat(platform, chatId);
|
|
2592
|
+
if (existing) {
|
|
2593
|
+
this.conversations.updateTimestamp(existing.id);
|
|
2594
|
+
return existing;
|
|
2595
|
+
}
|
|
2596
|
+
return this.conversations.create(platform, chatId, userId);
|
|
2597
|
+
}
|
|
2598
|
+
addMessage(conversationId, role, content, toolCalls) {
|
|
2599
|
+
return this.conversations.addMessage(conversationId, role, content, toolCalls);
|
|
2600
|
+
}
|
|
2601
|
+
getHistory(conversationId, limit = 20) {
|
|
2602
|
+
return this.conversations.getMessages(conversationId, limit);
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
|
|
2608
|
+
// ../core/dist/message-pipeline.js
|
|
2609
|
+
var MAX_TOOL_ITERATIONS, MessagePipeline;
|
|
2610
|
+
var init_message_pipeline = __esm({
|
|
2611
|
+
"../core/dist/message-pipeline.js"() {
|
|
2612
|
+
"use strict";
|
|
2613
|
+
init_dist4();
|
|
2614
|
+
MAX_TOOL_ITERATIONS = 10;
|
|
2615
|
+
MessagePipeline = class {
|
|
2616
|
+
llm;
|
|
2617
|
+
conversationManager;
|
|
2618
|
+
users;
|
|
2619
|
+
logger;
|
|
2620
|
+
skillRegistry;
|
|
2621
|
+
skillSandbox;
|
|
2622
|
+
securityManager;
|
|
2623
|
+
promptBuilder;
|
|
2624
|
+
constructor(llm, conversationManager, users, logger, skillRegistry, skillSandbox, securityManager) {
|
|
2625
|
+
this.llm = llm;
|
|
2626
|
+
this.conversationManager = conversationManager;
|
|
2627
|
+
this.users = users;
|
|
2628
|
+
this.logger = logger;
|
|
2629
|
+
this.skillRegistry = skillRegistry;
|
|
2630
|
+
this.skillSandbox = skillSandbox;
|
|
2631
|
+
this.securityManager = securityManager;
|
|
2632
|
+
this.promptBuilder = new PromptBuilder();
|
|
2633
|
+
}
|
|
2634
|
+
async process(message) {
|
|
2635
|
+
const startTime = Date.now();
|
|
2636
|
+
this.logger.info({ platform: message.platform, userId: message.userId, chatId: message.chatId }, "Processing message");
|
|
2637
|
+
try {
|
|
2638
|
+
const user = this.users.findOrCreate(message.platform, message.userId, message.userName, message.displayName);
|
|
2639
|
+
const conversation = this.conversationManager.getOrCreateConversation(message.platform, message.chatId, user.id);
|
|
2640
|
+
const history = this.conversationManager.getHistory(conversation.id);
|
|
2641
|
+
this.conversationManager.addMessage(conversation.id, "user", message.text);
|
|
2642
|
+
const system = this.promptBuilder.buildSystemPrompt();
|
|
2643
|
+
const messages = this.promptBuilder.buildMessages(history);
|
|
2644
|
+
messages.push({ role: "user", content: message.text });
|
|
2645
|
+
const tools = this.skillRegistry ? this.promptBuilder.buildTools(this.skillRegistry.getAll().map((s) => s.metadata)) : void 0;
|
|
2646
|
+
let response;
|
|
2647
|
+
let iteration = 0;
|
|
2648
|
+
while (true) {
|
|
2649
|
+
response = await this.llm.complete({
|
|
2650
|
+
messages,
|
|
2651
|
+
system,
|
|
2652
|
+
tools: tools && tools.length > 0 ? tools : void 0
|
|
2653
|
+
});
|
|
2654
|
+
if (!response.toolCalls || response.toolCalls.length === 0 || iteration >= MAX_TOOL_ITERATIONS) {
|
|
2655
|
+
if (iteration >= MAX_TOOL_ITERATIONS && response.toolCalls?.length) {
|
|
2656
|
+
this.logger.warn({ iteration }, "Max tool iterations reached, stopping loop");
|
|
2657
|
+
}
|
|
2658
|
+
break;
|
|
2659
|
+
}
|
|
2660
|
+
iteration++;
|
|
2661
|
+
this.logger.info({ iteration, toolCalls: response.toolCalls.length }, "Processing tool calls");
|
|
2662
|
+
const assistantContent = [];
|
|
2663
|
+
if (response.content) {
|
|
2664
|
+
assistantContent.push({ type: "text", text: response.content });
|
|
2665
|
+
}
|
|
2666
|
+
for (const tc of response.toolCalls) {
|
|
2667
|
+
assistantContent.push({
|
|
2668
|
+
type: "tool_use",
|
|
2669
|
+
id: tc.id,
|
|
2670
|
+
name: tc.name,
|
|
2671
|
+
input: tc.input
|
|
2672
|
+
});
|
|
2673
|
+
}
|
|
2674
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
2675
|
+
const toolResultBlocks = [];
|
|
2676
|
+
for (const toolCall of response.toolCalls) {
|
|
2677
|
+
const result = await this.executeToolCall(toolCall, {
|
|
2678
|
+
userId: user.id,
|
|
2679
|
+
chatId: message.chatId,
|
|
2680
|
+
platform: message.platform,
|
|
2681
|
+
conversationId: conversation.id
|
|
2682
|
+
});
|
|
2683
|
+
toolResultBlocks.push({
|
|
2684
|
+
type: "tool_result",
|
|
2685
|
+
tool_use_id: toolCall.id,
|
|
2686
|
+
content: result.content,
|
|
2687
|
+
is_error: result.isError
|
|
2688
|
+
});
|
|
2689
|
+
}
|
|
2690
|
+
messages.push({ role: "user", content: toolResultBlocks });
|
|
2691
|
+
}
|
|
2692
|
+
const responseText = response.content || "(no response)";
|
|
2693
|
+
this.conversationManager.addMessage(conversation.id, "assistant", responseText, response.toolCalls ? JSON.stringify(response.toolCalls) : void 0);
|
|
2694
|
+
const duration = Date.now() - startTime;
|
|
2695
|
+
this.logger.info({ duration, tokens: response.usage, stopReason: response.stopReason, toolIterations: iteration }, "Message processed");
|
|
2696
|
+
return responseText;
|
|
2697
|
+
} catch (error) {
|
|
2698
|
+
this.logger.error({ error }, "Failed to process message");
|
|
2699
|
+
throw error;
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
async executeToolCall(toolCall, context) {
|
|
2703
|
+
const skill = this.skillRegistry?.get(toolCall.name);
|
|
2704
|
+
if (!skill) {
|
|
2705
|
+
this.logger.warn({ tool: toolCall.name }, "Unknown skill requested");
|
|
2706
|
+
return { content: `Error: Unknown tool "${toolCall.name}"`, isError: true };
|
|
2707
|
+
}
|
|
2708
|
+
if (this.securityManager) {
|
|
2709
|
+
const evaluation = this.securityManager.evaluate({
|
|
2710
|
+
userId: context.userId,
|
|
2711
|
+
action: toolCall.name,
|
|
2712
|
+
riskLevel: skill.metadata.riskLevel,
|
|
2713
|
+
platform: context.platform,
|
|
2714
|
+
chatId: context.chatId
|
|
2715
|
+
});
|
|
2716
|
+
if (!evaluation.allowed) {
|
|
2717
|
+
this.logger.warn({ tool: toolCall.name, reason: evaluation.reason, rule: evaluation.matchedRule?.id }, "Skill execution denied by security rules");
|
|
2718
|
+
return {
|
|
2719
|
+
content: `Access denied: ${evaluation.reason}`,
|
|
2720
|
+
isError: true
|
|
2721
|
+
};
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
if (this.skillSandbox) {
|
|
2725
|
+
const result = await this.skillSandbox.execute(skill, toolCall.input, context);
|
|
2726
|
+
return {
|
|
2727
|
+
content: result.display ?? (result.success ? JSON.stringify(result.data) : result.error ?? "Unknown error"),
|
|
2728
|
+
isError: !result.success
|
|
2729
|
+
};
|
|
2730
|
+
}
|
|
2731
|
+
try {
|
|
2732
|
+
const result = await skill.execute(toolCall.input, context);
|
|
2733
|
+
return {
|
|
2734
|
+
content: result.display ?? (result.success ? JSON.stringify(result.data) : result.error ?? "Unknown error"),
|
|
2735
|
+
isError: !result.success
|
|
2736
|
+
};
|
|
2737
|
+
} catch (error) {
|
|
2738
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2739
|
+
return { content: `Skill execution failed: ${msg}`, isError: true };
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
});
|
|
2745
|
+
|
|
2746
|
+
// ../messaging/dist/adapter.js
|
|
2747
|
+
import { EventEmitter } from "node:events";
|
|
2748
|
+
var MessagingAdapter;
|
|
2749
|
+
var init_adapter = __esm({
|
|
2750
|
+
"../messaging/dist/adapter.js"() {
|
|
2751
|
+
"use strict";
|
|
2752
|
+
MessagingAdapter = class extends EventEmitter {
|
|
2753
|
+
status = "disconnected";
|
|
2754
|
+
getStatus() {
|
|
2755
|
+
return this.status;
|
|
2756
|
+
}
|
|
2757
|
+
};
|
|
2758
|
+
}
|
|
2759
|
+
});
|
|
2760
|
+
|
|
2761
|
+
// ../messaging/dist/adapters/telegram.js
|
|
2762
|
+
import { Bot } from "grammy";
|
|
2763
|
+
function mapParseMode(mode) {
|
|
2764
|
+
if (mode === "markdown")
|
|
2765
|
+
return "MarkdownV2";
|
|
2766
|
+
if (mode === "html")
|
|
2767
|
+
return "HTML";
|
|
2768
|
+
return void 0;
|
|
2769
|
+
}
|
|
2770
|
+
var TelegramAdapter;
|
|
2771
|
+
var init_telegram = __esm({
|
|
2772
|
+
"../messaging/dist/adapters/telegram.js"() {
|
|
2773
|
+
"use strict";
|
|
2774
|
+
init_adapter();
|
|
2775
|
+
TelegramAdapter = class extends MessagingAdapter {
|
|
2776
|
+
platform = "telegram";
|
|
2777
|
+
bot;
|
|
2778
|
+
constructor(token) {
|
|
2779
|
+
super();
|
|
2780
|
+
this.bot = new Bot(token);
|
|
2781
|
+
}
|
|
2782
|
+
async connect() {
|
|
2783
|
+
this.status = "connecting";
|
|
2784
|
+
this.bot.on("message:text", (ctx) => {
|
|
2785
|
+
const msg = ctx.message;
|
|
2786
|
+
const normalized = {
|
|
2787
|
+
id: String(msg.message_id),
|
|
2788
|
+
platform: "telegram",
|
|
2789
|
+
chatId: String(msg.chat.id),
|
|
2790
|
+
chatType: msg.chat.type === "private" ? "dm" : "group",
|
|
2791
|
+
userId: String(msg.from.id),
|
|
2792
|
+
userName: msg.from.username ?? String(msg.from.id),
|
|
2793
|
+
displayName: [msg.from.first_name, msg.from.last_name].filter(Boolean).join(" "),
|
|
2794
|
+
text: msg.text,
|
|
2795
|
+
timestamp: new Date(msg.date * 1e3),
|
|
2796
|
+
replyToMessageId: msg.reply_to_message ? String(msg.reply_to_message.message_id) : void 0
|
|
2797
|
+
};
|
|
2798
|
+
this.emit("message", normalized);
|
|
2799
|
+
});
|
|
2800
|
+
this.bot.catch((err) => {
|
|
2801
|
+
this.emit("error", err.error);
|
|
2802
|
+
});
|
|
2803
|
+
this.bot.start({
|
|
2804
|
+
onStart: () => {
|
|
2805
|
+
this.status = "connected";
|
|
2806
|
+
this.emit("connected");
|
|
2807
|
+
}
|
|
2808
|
+
});
|
|
2809
|
+
}
|
|
2810
|
+
async disconnect() {
|
|
2811
|
+
await this.bot.stop();
|
|
2812
|
+
this.status = "disconnected";
|
|
2813
|
+
this.emit("disconnected");
|
|
2814
|
+
}
|
|
2815
|
+
async sendMessage(chatId, text, options) {
|
|
2816
|
+
const result = await this.bot.api.sendMessage(Number(chatId), text, {
|
|
2817
|
+
reply_to_message_id: options?.replyToMessageId ? Number(options.replyToMessageId) : void 0,
|
|
2818
|
+
parse_mode: mapParseMode(options?.parseMode)
|
|
2819
|
+
});
|
|
2820
|
+
return String(result.message_id);
|
|
2821
|
+
}
|
|
2822
|
+
async editMessage(chatId, messageId, text) {
|
|
2823
|
+
await this.bot.api.editMessageText(Number(chatId), Number(messageId), text);
|
|
2824
|
+
}
|
|
2825
|
+
async deleteMessage(chatId, messageId) {
|
|
2826
|
+
await this.bot.api.deleteMessage(Number(chatId), Number(messageId));
|
|
2827
|
+
}
|
|
2828
|
+
};
|
|
2829
|
+
}
|
|
2830
|
+
});
|
|
2831
|
+
|
|
2832
|
+
// ../messaging/dist/adapters/discord.js
|
|
2833
|
+
import { Client, GatewayIntentBits, Events } from "discord.js";
|
|
2834
|
+
var DiscordAdapter;
|
|
2835
|
+
var init_discord = __esm({
|
|
2836
|
+
"../messaging/dist/adapters/discord.js"() {
|
|
2837
|
+
"use strict";
|
|
2838
|
+
init_adapter();
|
|
2839
|
+
DiscordAdapter = class extends MessagingAdapter {
|
|
2840
|
+
platform = "discord";
|
|
2841
|
+
client = null;
|
|
2842
|
+
token;
|
|
2843
|
+
constructor(token) {
|
|
2844
|
+
super();
|
|
2845
|
+
this.token = token;
|
|
2846
|
+
}
|
|
2847
|
+
async connect() {
|
|
2848
|
+
this.status = "connecting";
|
|
2849
|
+
this.client = new Client({
|
|
2850
|
+
intents: [
|
|
2851
|
+
GatewayIntentBits.Guilds,
|
|
2852
|
+
GatewayIntentBits.GuildMessages,
|
|
2853
|
+
GatewayIntentBits.MessageContent,
|
|
2854
|
+
GatewayIntentBits.DirectMessages
|
|
2855
|
+
]
|
|
2856
|
+
});
|
|
2857
|
+
this.client.on(Events.MessageCreate, (message) => {
|
|
2858
|
+
if (message.author.bot)
|
|
2859
|
+
return;
|
|
2860
|
+
const normalized = {
|
|
2861
|
+
id: message.id,
|
|
2862
|
+
platform: "discord",
|
|
2863
|
+
chatId: message.channelId,
|
|
2864
|
+
chatType: message.channel.isDMBased() ? "dm" : "group",
|
|
2865
|
+
userId: message.author.id,
|
|
2866
|
+
userName: message.author.username,
|
|
2867
|
+
displayName: message.author.displayName,
|
|
2868
|
+
text: message.content,
|
|
2869
|
+
timestamp: message.createdAt,
|
|
2870
|
+
replyToMessageId: message.reference?.messageId ?? void 0
|
|
2871
|
+
};
|
|
2872
|
+
this.emit("message", normalized);
|
|
2873
|
+
});
|
|
2874
|
+
this.client.on(Events.ClientReady, () => {
|
|
2875
|
+
this.status = "connected";
|
|
2876
|
+
this.emit("connected");
|
|
2877
|
+
});
|
|
2878
|
+
this.client.on(Events.Error, (error) => {
|
|
2879
|
+
this.emit("error", error);
|
|
2880
|
+
});
|
|
2881
|
+
await this.client.login(this.token);
|
|
2882
|
+
}
|
|
2883
|
+
async disconnect() {
|
|
2884
|
+
this.client?.destroy();
|
|
2885
|
+
this.client = null;
|
|
2886
|
+
this.status = "disconnected";
|
|
2887
|
+
this.emit("disconnected");
|
|
2888
|
+
}
|
|
2889
|
+
async sendMessage(chatId, text, options) {
|
|
2890
|
+
if (!this.client)
|
|
2891
|
+
throw new Error("Client is not connected");
|
|
2892
|
+
const channel = await this.client.channels.fetch(chatId);
|
|
2893
|
+
if (!channel?.isTextBased() || !("send" in channel)) {
|
|
2894
|
+
throw new Error(`Channel ${chatId} is not a text channel`);
|
|
2895
|
+
}
|
|
2896
|
+
if (options?.replyToMessageId) {
|
|
2897
|
+
const original = await channel.messages.fetch(options.replyToMessageId);
|
|
2898
|
+
const reply = await original.reply(text);
|
|
2899
|
+
return reply.id;
|
|
2900
|
+
}
|
|
2901
|
+
const message = await channel.send(text);
|
|
2902
|
+
return message.id;
|
|
2903
|
+
}
|
|
2904
|
+
async editMessage(chatId, messageId, text) {
|
|
2905
|
+
if (!this.client)
|
|
2906
|
+
throw new Error("Client is not connected");
|
|
2907
|
+
const channel = await this.client.channels.fetch(chatId);
|
|
2908
|
+
if (!channel?.isTextBased() || !("messages" in channel)) {
|
|
2909
|
+
throw new Error(`Channel ${chatId} is not a text channel`);
|
|
2910
|
+
}
|
|
2911
|
+
const message = await channel.messages.fetch(messageId);
|
|
2912
|
+
await message.edit(text);
|
|
2913
|
+
}
|
|
2914
|
+
async deleteMessage(chatId, messageId) {
|
|
2915
|
+
if (!this.client)
|
|
2916
|
+
throw new Error("Client is not connected");
|
|
2917
|
+
const channel = await this.client.channels.fetch(chatId);
|
|
2918
|
+
if (!channel?.isTextBased() || !("messages" in channel)) {
|
|
2919
|
+
throw new Error(`Channel ${chatId} is not a text channel`);
|
|
2920
|
+
}
|
|
2921
|
+
const message = await channel.messages.fetch(messageId);
|
|
2922
|
+
await message.delete();
|
|
2923
|
+
}
|
|
2924
|
+
};
|
|
2925
|
+
}
|
|
2926
|
+
});
|
|
2927
|
+
|
|
2928
|
+
// ../messaging/dist/adapters/matrix.js
|
|
2929
|
+
var MatrixAdapter;
|
|
2930
|
+
var init_matrix = __esm({
|
|
2931
|
+
"../messaging/dist/adapters/matrix.js"() {
|
|
2932
|
+
"use strict";
|
|
2933
|
+
init_adapter();
|
|
2934
|
+
MatrixAdapter = class extends MessagingAdapter {
|
|
2935
|
+
platform = "matrix";
|
|
2936
|
+
client;
|
|
2937
|
+
homeserverUrl;
|
|
2938
|
+
accessToken;
|
|
2939
|
+
botUserId;
|
|
2940
|
+
constructor(homeserverUrl, accessToken, botUserId) {
|
|
2941
|
+
super();
|
|
2942
|
+
this.homeserverUrl = homeserverUrl;
|
|
2943
|
+
this.accessToken = accessToken;
|
|
2944
|
+
this.botUserId = botUserId;
|
|
2945
|
+
}
|
|
2946
|
+
async connect() {
|
|
2947
|
+
this.status = "connecting";
|
|
2948
|
+
const { MatrixClient, SimpleFsStorageProvider, AutojoinRoomsMixin } = await import("matrix-bot-sdk");
|
|
2949
|
+
const storageProvider = new SimpleFsStorageProvider("./data/matrix-storage");
|
|
2950
|
+
this.client = new MatrixClient(this.homeserverUrl, this.accessToken, storageProvider);
|
|
2951
|
+
AutojoinRoomsMixin.setupOnClient(this.client);
|
|
2952
|
+
this.client.on("room.message", (roomId, event) => {
|
|
2953
|
+
if (event.sender === this.botUserId)
|
|
2954
|
+
return;
|
|
2955
|
+
if (event.content?.msgtype !== "m.text")
|
|
2956
|
+
return;
|
|
2957
|
+
const normalized = {
|
|
2958
|
+
id: event.event_id,
|
|
2959
|
+
platform: "matrix",
|
|
2960
|
+
chatId: roomId,
|
|
2961
|
+
chatType: "group",
|
|
2962
|
+
userId: event.sender,
|
|
2963
|
+
userName: event.sender.split(":")[0].slice(1),
|
|
2964
|
+
text: event.content.body,
|
|
2965
|
+
timestamp: new Date(event.origin_server_ts),
|
|
2966
|
+
replyToMessageId: event.content["m.relates_to"]?.["m.in_reply_to"]?.event_id
|
|
2967
|
+
};
|
|
2968
|
+
this.emit("message", normalized);
|
|
2969
|
+
});
|
|
2970
|
+
await this.client.start();
|
|
2971
|
+
this.status = "connected";
|
|
2972
|
+
this.emit("connected");
|
|
2973
|
+
}
|
|
2974
|
+
async disconnect() {
|
|
2975
|
+
this.client.stop();
|
|
2976
|
+
this.status = "disconnected";
|
|
2977
|
+
this.emit("disconnected");
|
|
2978
|
+
}
|
|
2979
|
+
async sendMessage(chatId, text, _options) {
|
|
2980
|
+
const eventId = await this.client.sendText(chatId, text);
|
|
2981
|
+
return eventId;
|
|
2982
|
+
}
|
|
2983
|
+
async editMessage(chatId, messageId, text) {
|
|
2984
|
+
await this.client.sendEvent(chatId, "m.room.message", {
|
|
2985
|
+
"msgtype": "m.text",
|
|
2986
|
+
"body": "* " + text,
|
|
2987
|
+
"m.new_content": {
|
|
2988
|
+
msgtype: "m.text",
|
|
2989
|
+
body: text
|
|
2990
|
+
},
|
|
2991
|
+
"m.relates_to": {
|
|
2992
|
+
rel_type: "m.replace",
|
|
2993
|
+
event_id: messageId
|
|
2994
|
+
}
|
|
2995
|
+
});
|
|
2996
|
+
}
|
|
2997
|
+
async deleteMessage(chatId, messageId) {
|
|
2998
|
+
await this.client.redactEvent(chatId, messageId);
|
|
2999
|
+
}
|
|
3000
|
+
};
|
|
3001
|
+
}
|
|
3002
|
+
});
|
|
3003
|
+
|
|
3004
|
+
// ../messaging/dist/adapters/whatsapp.js
|
|
3005
|
+
var WhatsAppAdapter;
|
|
3006
|
+
var init_whatsapp = __esm({
|
|
3007
|
+
"../messaging/dist/adapters/whatsapp.js"() {
|
|
3008
|
+
"use strict";
|
|
3009
|
+
init_adapter();
|
|
3010
|
+
WhatsAppAdapter = class extends MessagingAdapter {
|
|
3011
|
+
platform = "whatsapp";
|
|
3012
|
+
socket;
|
|
3013
|
+
dataPath;
|
|
3014
|
+
constructor(dataPath) {
|
|
3015
|
+
super();
|
|
3016
|
+
this.dataPath = dataPath;
|
|
3017
|
+
}
|
|
3018
|
+
async connect() {
|
|
3019
|
+
this.status = "connecting";
|
|
3020
|
+
const baileys = await import("@whiskeysockets/baileys");
|
|
3021
|
+
const { makeWASocket, useMultiFileAuthState, DisconnectReason } = baileys.default ?? baileys;
|
|
3022
|
+
const { state, saveCreds } = await useMultiFileAuthState(this.dataPath);
|
|
3023
|
+
this.socket = makeWASocket({
|
|
3024
|
+
auth: state,
|
|
3025
|
+
printQRInTerminal: true
|
|
3026
|
+
});
|
|
3027
|
+
this.socket.ev.on("creds.update", saveCreds);
|
|
3028
|
+
this.socket.ev.on("connection.update", (update) => {
|
|
3029
|
+
if (update.connection === "open") {
|
|
3030
|
+
this.status = "connected";
|
|
3031
|
+
this.emit("connected");
|
|
3032
|
+
}
|
|
3033
|
+
if (update.connection === "close") {
|
|
3034
|
+
const statusCode = update.lastDisconnect?.error?.output?.statusCode;
|
|
3035
|
+
const shouldReconnect = statusCode !== DisconnectReason.loggedOut;
|
|
3036
|
+
this.status = "disconnected";
|
|
3037
|
+
this.emit("disconnected");
|
|
3038
|
+
if (shouldReconnect) {
|
|
3039
|
+
this.connect();
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
});
|
|
3043
|
+
this.socket.ev.on("messages.upsert", ({ messages, type }) => {
|
|
3044
|
+
if (type !== "notify")
|
|
3045
|
+
return;
|
|
3046
|
+
for (const message of messages) {
|
|
3047
|
+
if (!message.message)
|
|
3048
|
+
continue;
|
|
3049
|
+
if (message.key.fromMe)
|
|
3050
|
+
continue;
|
|
3051
|
+
const text = message.message.conversation ?? message.message.extendedTextMessage?.text;
|
|
3052
|
+
if (!text)
|
|
3053
|
+
continue;
|
|
3054
|
+
const normalized = {
|
|
3055
|
+
id: message.key.id ?? "",
|
|
3056
|
+
platform: "whatsapp",
|
|
3057
|
+
chatId: message.key.remoteJid ?? "",
|
|
3058
|
+
chatType: message.key.remoteJid?.endsWith("@g.us") ? "group" : "dm",
|
|
3059
|
+
userId: message.key.participant ?? message.key.remoteJid ?? "",
|
|
3060
|
+
userName: message.pushName ?? message.key.participant ?? message.key.remoteJid ?? "",
|
|
3061
|
+
text,
|
|
3062
|
+
timestamp: new Date(message.messageTimestamp * 1e3),
|
|
3063
|
+
replyToMessageId: message.message.extendedTextMessage?.contextInfo?.stanzaId ?? void 0
|
|
3064
|
+
};
|
|
3065
|
+
this.emit("message", normalized);
|
|
3066
|
+
}
|
|
3067
|
+
});
|
|
3068
|
+
}
|
|
3069
|
+
async disconnect() {
|
|
3070
|
+
this.socket?.end(void 0);
|
|
3071
|
+
this.socket = void 0;
|
|
3072
|
+
this.status = "disconnected";
|
|
3073
|
+
this.emit("disconnected");
|
|
3074
|
+
}
|
|
3075
|
+
async sendMessage(chatId, text, options) {
|
|
3076
|
+
const msg = await this.socket.sendMessage(chatId, { text }, options?.replyToMessageId ? {
|
|
3077
|
+
quoted: {
|
|
3078
|
+
key: { remoteJid: chatId, id: options.replyToMessageId },
|
|
3079
|
+
message: {}
|
|
3080
|
+
}
|
|
3081
|
+
} : void 0);
|
|
3082
|
+
return msg?.key?.id ?? "";
|
|
3083
|
+
}
|
|
3084
|
+
async editMessage(chatId, messageId, text) {
|
|
3085
|
+
await this.socket.sendMessage(chatId, {
|
|
3086
|
+
text,
|
|
3087
|
+
edit: {
|
|
3088
|
+
remoteJid: chatId,
|
|
3089
|
+
id: messageId,
|
|
3090
|
+
fromMe: true
|
|
3091
|
+
}
|
|
3092
|
+
});
|
|
3093
|
+
}
|
|
3094
|
+
async deleteMessage(chatId, messageId) {
|
|
3095
|
+
await this.socket.sendMessage(chatId, {
|
|
3096
|
+
delete: {
|
|
3097
|
+
remoteJid: chatId,
|
|
3098
|
+
id: messageId,
|
|
3099
|
+
fromMe: true
|
|
3100
|
+
}
|
|
3101
|
+
});
|
|
3102
|
+
}
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
});
|
|
3106
|
+
|
|
3107
|
+
// ../messaging/dist/adapters/signal.js
|
|
3108
|
+
var SignalAdapter;
|
|
3109
|
+
var init_signal = __esm({
|
|
3110
|
+
"../messaging/dist/adapters/signal.js"() {
|
|
3111
|
+
"use strict";
|
|
3112
|
+
init_adapter();
|
|
3113
|
+
SignalAdapter = class extends MessagingAdapter {
|
|
3114
|
+
apiUrl;
|
|
3115
|
+
phoneNumber;
|
|
3116
|
+
platform = "signal";
|
|
3117
|
+
pollingInterval;
|
|
3118
|
+
constructor(apiUrl, phoneNumber) {
|
|
3119
|
+
super();
|
|
3120
|
+
this.apiUrl = apiUrl;
|
|
3121
|
+
this.phoneNumber = phoneNumber;
|
|
3122
|
+
}
|
|
3123
|
+
async connect() {
|
|
3124
|
+
this.status = "connecting";
|
|
3125
|
+
try {
|
|
3126
|
+
const res = await fetch(`${this.apiUrl}/v1/about`);
|
|
3127
|
+
if (!res.ok) {
|
|
3128
|
+
throw new Error(`Signal API not reachable: ${res.status}`);
|
|
3129
|
+
}
|
|
3130
|
+
this.pollingInterval = setInterval(() => {
|
|
3131
|
+
this.pollMessages().catch((err) => {
|
|
3132
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
3133
|
+
});
|
|
3134
|
+
}, 2e3);
|
|
3135
|
+
this.status = "connected";
|
|
3136
|
+
this.emit("connected");
|
|
3137
|
+
} catch (error) {
|
|
3138
|
+
this.status = "error";
|
|
3139
|
+
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
async disconnect() {
|
|
3143
|
+
if (this.pollingInterval) {
|
|
3144
|
+
clearInterval(this.pollingInterval);
|
|
3145
|
+
this.pollingInterval = void 0;
|
|
3146
|
+
}
|
|
3147
|
+
this.status = "disconnected";
|
|
3148
|
+
this.emit("disconnected");
|
|
3149
|
+
}
|
|
3150
|
+
async sendMessage(chatId, text, _options) {
|
|
3151
|
+
const isGroup = chatId.startsWith("group.");
|
|
3152
|
+
const body = {
|
|
3153
|
+
message: text,
|
|
3154
|
+
number: this.phoneNumber
|
|
3155
|
+
};
|
|
3156
|
+
if (isGroup) {
|
|
3157
|
+
body.recipients = [chatId.replace("group.", "")];
|
|
3158
|
+
} else {
|
|
3159
|
+
body.recipients = [chatId];
|
|
3160
|
+
}
|
|
3161
|
+
const res = await fetch(`${this.apiUrl}/v2/send`, {
|
|
3162
|
+
method: "POST",
|
|
3163
|
+
headers: { "Content-Type": "application/json" },
|
|
3164
|
+
body: JSON.stringify(body)
|
|
3165
|
+
});
|
|
3166
|
+
if (!res.ok) {
|
|
3167
|
+
throw new Error(`Signal send failed: ${res.status} ${await res.text()}`);
|
|
3168
|
+
}
|
|
3169
|
+
const result = await res.json();
|
|
3170
|
+
return String(result.timestamp ?? Date.now());
|
|
3171
|
+
}
|
|
3172
|
+
async editMessage(_chatId, _messageId, _text) {
|
|
3173
|
+
throw new Error("Signal does not support message editing");
|
|
3174
|
+
}
|
|
3175
|
+
async deleteMessage(chatId, messageId) {
|
|
3176
|
+
const body = {
|
|
3177
|
+
number: this.phoneNumber,
|
|
3178
|
+
recipients: [chatId],
|
|
3179
|
+
timestamp: Number(messageId)
|
|
3180
|
+
};
|
|
3181
|
+
const res = await fetch(`${this.apiUrl}/v1/deleteMessage`, {
|
|
3182
|
+
method: "POST",
|
|
3183
|
+
headers: { "Content-Type": "application/json" },
|
|
3184
|
+
body: JSON.stringify(body)
|
|
3185
|
+
});
|
|
3186
|
+
if (!res.ok) {
|
|
3187
|
+
throw new Error(`Signal delete failed: ${res.status} ${await res.text()}`);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
async pollMessages() {
|
|
3191
|
+
const res = await fetch(`${this.apiUrl}/v1/receive/${this.phoneNumber}`);
|
|
3192
|
+
if (!res.ok)
|
|
3193
|
+
return;
|
|
3194
|
+
const messages = await res.json();
|
|
3195
|
+
for (const envelope of messages) {
|
|
3196
|
+
const dataMessage = envelope.envelope?.dataMessage;
|
|
3197
|
+
if (!dataMessage?.message)
|
|
3198
|
+
continue;
|
|
3199
|
+
const data = envelope.envelope;
|
|
3200
|
+
const chatId = dataMessage.groupInfo?.groupId ? `group.${dataMessage.groupInfo.groupId}` : data.sourceNumber ?? data.source ?? "";
|
|
3201
|
+
const normalized = {
|
|
3202
|
+
id: String(dataMessage.timestamp ?? Date.now()),
|
|
3203
|
+
platform: "signal",
|
|
3204
|
+
chatId,
|
|
3205
|
+
chatType: dataMessage.groupInfo ? "group" : "dm",
|
|
3206
|
+
userId: data.sourceNumber ?? data.source ?? "",
|
|
3207
|
+
userName: data.sourceName ?? data.sourceNumber ?? data.source ?? "",
|
|
3208
|
+
displayName: data.sourceName,
|
|
3209
|
+
text: dataMessage.message,
|
|
3210
|
+
timestamp: new Date(dataMessage.timestamp ?? Date.now())
|
|
3211
|
+
};
|
|
3212
|
+
this.emit("message", normalized);
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
});
|
|
3218
|
+
|
|
3219
|
+
// ../messaging/dist/index.js
|
|
3220
|
+
var dist_exports = {};
|
|
3221
|
+
__export(dist_exports, {
|
|
3222
|
+
DiscordAdapter: () => DiscordAdapter,
|
|
3223
|
+
MatrixAdapter: () => MatrixAdapter,
|
|
3224
|
+
MessagingAdapter: () => MessagingAdapter,
|
|
3225
|
+
SignalAdapter: () => SignalAdapter,
|
|
3226
|
+
TelegramAdapter: () => TelegramAdapter,
|
|
3227
|
+
WhatsAppAdapter: () => WhatsAppAdapter
|
|
3228
|
+
});
|
|
3229
|
+
var init_dist7 = __esm({
|
|
3230
|
+
"../messaging/dist/index.js"() {
|
|
3231
|
+
"use strict";
|
|
3232
|
+
init_adapter();
|
|
3233
|
+
init_telegram();
|
|
3234
|
+
init_discord();
|
|
3235
|
+
init_matrix();
|
|
3236
|
+
init_whatsapp();
|
|
3237
|
+
init_signal();
|
|
3238
|
+
}
|
|
3239
|
+
});
|
|
3240
|
+
|
|
3241
|
+
// ../core/dist/alfred.js
|
|
3242
|
+
var Alfred;
|
|
3243
|
+
var init_alfred = __esm({
|
|
3244
|
+
"../core/dist/alfred.js"() {
|
|
3245
|
+
"use strict";
|
|
3246
|
+
init_dist2();
|
|
3247
|
+
init_dist3();
|
|
3248
|
+
init_dist4();
|
|
3249
|
+
init_dist5();
|
|
3250
|
+
init_dist6();
|
|
3251
|
+
init_conversation_manager();
|
|
3252
|
+
init_message_pipeline();
|
|
3253
|
+
Alfred = class {
|
|
3254
|
+
config;
|
|
3255
|
+
logger;
|
|
3256
|
+
database;
|
|
3257
|
+
pipeline;
|
|
3258
|
+
adapters = /* @__PURE__ */ new Map();
|
|
3259
|
+
constructor(config) {
|
|
3260
|
+
this.config = config;
|
|
3261
|
+
this.logger = createLogger("alfred", config.logger.level);
|
|
3262
|
+
}
|
|
3263
|
+
async initialize() {
|
|
3264
|
+
this.logger.info("Initializing Alfred...");
|
|
3265
|
+
this.database = new Database(this.config.storage.path);
|
|
3266
|
+
const db = this.database.getDb();
|
|
3267
|
+
const conversationRepo = new ConversationRepository(db);
|
|
3268
|
+
const userRepo = new UserRepository(db);
|
|
3269
|
+
const auditRepo = new AuditRepository(db);
|
|
3270
|
+
this.logger.info("Storage initialized");
|
|
3271
|
+
const ruleEngine = new RuleEngine();
|
|
3272
|
+
const securityManager = new SecurityManager(ruleEngine, auditRepo, this.logger.child({ component: "security" }));
|
|
3273
|
+
this.logger.info("Security engine initialized");
|
|
3274
|
+
const skillRegistry = new SkillRegistry();
|
|
3275
|
+
skillRegistry.register(new CalculatorSkill());
|
|
3276
|
+
skillRegistry.register(new SystemInfoSkill());
|
|
3277
|
+
skillRegistry.register(new WebSearchSkill());
|
|
3278
|
+
skillRegistry.register(new ReminderSkill());
|
|
3279
|
+
skillRegistry.register(new NoteSkill());
|
|
3280
|
+
skillRegistry.register(new SummarizeSkill());
|
|
3281
|
+
skillRegistry.register(new TranslateSkill());
|
|
3282
|
+
skillRegistry.register(new WeatherSkill());
|
|
3283
|
+
this.logger.info({ skills: skillRegistry.getAll().map((s) => s.metadata.name) }, "Skills registered");
|
|
3284
|
+
const skillSandbox = new SkillSandbox(this.logger.child({ component: "sandbox" }));
|
|
3285
|
+
const llmProvider = createLLMProvider(this.config.llm);
|
|
3286
|
+
await llmProvider.initialize();
|
|
3287
|
+
this.logger.info({ provider: this.config.llm.provider, model: this.config.llm.model }, "LLM provider initialized");
|
|
3288
|
+
const conversationManager = new ConversationManager(conversationRepo);
|
|
3289
|
+
this.pipeline = new MessagePipeline(llmProvider, conversationManager, userRepo, this.logger.child({ component: "pipeline" }), skillRegistry, skillSandbox, securityManager);
|
|
3290
|
+
await this.initializeAdapters();
|
|
3291
|
+
this.logger.info("Alfred initialized");
|
|
3292
|
+
}
|
|
3293
|
+
async initializeAdapters() {
|
|
3294
|
+
const { config } = this;
|
|
3295
|
+
if (config.telegram.enabled && config.telegram.token) {
|
|
3296
|
+
const { TelegramAdapter: TelegramAdapter2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports));
|
|
3297
|
+
this.adapters.set("telegram", new TelegramAdapter2(config.telegram.token));
|
|
3298
|
+
this.logger.info("Telegram adapter registered");
|
|
3299
|
+
}
|
|
3300
|
+
if (config.discord?.enabled && config.discord.token) {
|
|
3301
|
+
const { DiscordAdapter: DiscordAdapter2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports));
|
|
3302
|
+
this.adapters.set("discord", new DiscordAdapter2(config.discord.token));
|
|
3303
|
+
this.logger.info("Discord adapter registered");
|
|
3304
|
+
}
|
|
3305
|
+
if (config.whatsapp?.enabled) {
|
|
3306
|
+
const { WhatsAppAdapter: WhatsAppAdapter2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports));
|
|
3307
|
+
this.adapters.set("whatsapp", new WhatsAppAdapter2(config.whatsapp.dataPath));
|
|
3308
|
+
this.logger.info("WhatsApp adapter registered");
|
|
3309
|
+
}
|
|
3310
|
+
if (config.matrix?.enabled && config.matrix.accessToken) {
|
|
3311
|
+
const { MatrixAdapter: MatrixAdapter2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports));
|
|
3312
|
+
this.adapters.set("matrix", new MatrixAdapter2(config.matrix.homeserverUrl, config.matrix.accessToken, config.matrix.userId));
|
|
3313
|
+
this.logger.info("Matrix adapter registered");
|
|
3314
|
+
}
|
|
3315
|
+
if (config.signal?.enabled && config.signal.phoneNumber) {
|
|
3316
|
+
const { SignalAdapter: SignalAdapter2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports));
|
|
3317
|
+
this.adapters.set("signal", new SignalAdapter2(config.signal.apiUrl, config.signal.phoneNumber));
|
|
3318
|
+
this.logger.info("Signal adapter registered");
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
async start() {
|
|
3322
|
+
this.logger.info("Starting Alfred...");
|
|
3323
|
+
for (const [platform, adapter] of this.adapters) {
|
|
3324
|
+
this.setupAdapterHandlers(platform, adapter);
|
|
3325
|
+
await adapter.connect();
|
|
3326
|
+
this.logger.info({ platform }, "Adapter connected");
|
|
3327
|
+
}
|
|
3328
|
+
if (this.adapters.size === 0) {
|
|
3329
|
+
this.logger.warn("No messaging adapters enabled. Configure at least one platform.");
|
|
3330
|
+
}
|
|
3331
|
+
this.logger.info(`Alfred is running with ${this.adapters.size} adapter(s)`);
|
|
3332
|
+
}
|
|
3333
|
+
async stop() {
|
|
3334
|
+
this.logger.info("Stopping Alfred...");
|
|
3335
|
+
for (const [platform, adapter] of this.adapters) {
|
|
3336
|
+
try {
|
|
3337
|
+
await adapter.disconnect();
|
|
3338
|
+
this.logger.info({ platform }, "Adapter disconnected");
|
|
3339
|
+
} catch (error) {
|
|
3340
|
+
this.logger.error({ platform, error }, "Failed to disconnect adapter");
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
this.database.close();
|
|
3344
|
+
this.logger.info("Alfred stopped");
|
|
3345
|
+
}
|
|
3346
|
+
setupAdapterHandlers(platform, adapter) {
|
|
3347
|
+
adapter.on("message", async (message) => {
|
|
3348
|
+
try {
|
|
3349
|
+
const response = await this.pipeline.process(message);
|
|
3350
|
+
await adapter.sendMessage(message.chatId, response);
|
|
3351
|
+
} catch (error) {
|
|
3352
|
+
this.logger.error({ platform, error, chatId: message.chatId }, "Failed to handle message");
|
|
3353
|
+
try {
|
|
3354
|
+
await adapter.sendMessage(message.chatId, "Sorry, I encountered an error processing your message. Please try again.");
|
|
3355
|
+
} catch (sendError) {
|
|
3356
|
+
this.logger.error({ error: sendError }, "Failed to send error message");
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
});
|
|
3360
|
+
adapter.on("error", (error) => {
|
|
3361
|
+
this.logger.error({ platform, error }, "Adapter error");
|
|
3362
|
+
});
|
|
3363
|
+
adapter.on("connected", () => {
|
|
3364
|
+
this.logger.info({ platform }, "Adapter connected");
|
|
3365
|
+
});
|
|
3366
|
+
adapter.on("disconnected", () => {
|
|
3367
|
+
this.logger.warn({ platform }, "Adapter disconnected");
|
|
3368
|
+
});
|
|
3369
|
+
}
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3372
|
+
});
|
|
3373
|
+
|
|
3374
|
+
// ../core/dist/index.js
|
|
3375
|
+
var init_dist8 = __esm({
|
|
3376
|
+
"../core/dist/index.js"() {
|
|
3377
|
+
"use strict";
|
|
3378
|
+
init_alfred();
|
|
3379
|
+
init_message_pipeline();
|
|
3380
|
+
init_conversation_manager();
|
|
3381
|
+
}
|
|
3382
|
+
});
|
|
3383
|
+
|
|
3384
|
+
// dist/commands/start.js
|
|
3385
|
+
var start_exports = {};
|
|
3386
|
+
__export(start_exports, {
|
|
3387
|
+
startCommand: () => startCommand
|
|
3388
|
+
});
|
|
3389
|
+
async function startCommand() {
|
|
3390
|
+
const configLoader = new ConfigLoader();
|
|
3391
|
+
let config;
|
|
3392
|
+
try {
|
|
3393
|
+
config = configLoader.loadConfig();
|
|
3394
|
+
} catch (error) {
|
|
3395
|
+
console.error("Failed to load configuration:", error.message);
|
|
3396
|
+
process.exit(1);
|
|
3397
|
+
}
|
|
3398
|
+
const logger = createLogger("cli", config.logger.level);
|
|
3399
|
+
logger.info({ name: config.name }, "Configuration loaded");
|
|
3400
|
+
const alfred = new Alfred(config);
|
|
3401
|
+
let isShuttingDown = false;
|
|
3402
|
+
const shutdown = async (signal) => {
|
|
3403
|
+
if (isShuttingDown)
|
|
3404
|
+
return;
|
|
3405
|
+
isShuttingDown = true;
|
|
3406
|
+
logger.info({ signal }, "Received shutdown signal");
|
|
3407
|
+
try {
|
|
3408
|
+
await alfred.stop();
|
|
3409
|
+
logger.info("Graceful shutdown complete");
|
|
3410
|
+
process.exit(0);
|
|
3411
|
+
} catch (err) {
|
|
3412
|
+
logger.error({ error: err }, "Error during shutdown");
|
|
3413
|
+
process.exit(1);
|
|
3414
|
+
}
|
|
3415
|
+
};
|
|
3416
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
3417
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
3418
|
+
process.on("uncaughtException", (err) => {
|
|
3419
|
+
logger.fatal({ error: err }, "Uncaught exception");
|
|
3420
|
+
shutdown("uncaughtException");
|
|
3421
|
+
});
|
|
3422
|
+
process.on("unhandledRejection", (reason) => {
|
|
3423
|
+
logger.fatal({ reason }, "Unhandled rejection");
|
|
3424
|
+
shutdown("unhandledRejection");
|
|
3425
|
+
});
|
|
3426
|
+
try {
|
|
3427
|
+
await alfred.initialize();
|
|
3428
|
+
await alfred.start();
|
|
3429
|
+
logger.info("Alfred is ready");
|
|
3430
|
+
} catch (error) {
|
|
3431
|
+
logger.fatal({ error }, "Failed to start Alfred");
|
|
3432
|
+
process.exit(1);
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
var init_start = __esm({
|
|
3436
|
+
"dist/commands/start.js"() {
|
|
3437
|
+
"use strict";
|
|
3438
|
+
init_dist();
|
|
3439
|
+
init_dist2();
|
|
3440
|
+
init_dist8();
|
|
3441
|
+
}
|
|
3442
|
+
});
|
|
3443
|
+
|
|
3444
|
+
// dist/commands/setup.js
|
|
3445
|
+
var setup_exports = {};
|
|
3446
|
+
__export(setup_exports, {
|
|
3447
|
+
setupCommand: () => setupCommand
|
|
3448
|
+
});
|
|
3449
|
+
import { createInterface } from "node:readline/promises";
|
|
3450
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
3451
|
+
import fs4 from "node:fs";
|
|
3452
|
+
import path4 from "node:path";
|
|
3453
|
+
import yaml2 from "js-yaml";
|
|
3454
|
+
function green(s) {
|
|
3455
|
+
return `${GREEN}${s}${RESET}`;
|
|
3456
|
+
}
|
|
3457
|
+
function yellow(s) {
|
|
3458
|
+
return `${YELLOW}${s}${RESET}`;
|
|
3459
|
+
}
|
|
3460
|
+
function cyan(s) {
|
|
3461
|
+
return `${CYAN}${s}${RESET}`;
|
|
3462
|
+
}
|
|
3463
|
+
function red(s) {
|
|
3464
|
+
return `${RED}${s}${RESET}`;
|
|
3465
|
+
}
|
|
3466
|
+
function bold(s) {
|
|
3467
|
+
return `${BOLD}${s}${RESET}`;
|
|
3468
|
+
}
|
|
3469
|
+
function dim(s) {
|
|
3470
|
+
return `${DIM}${s}${RESET}`;
|
|
3471
|
+
}
|
|
3472
|
+
function maskKey(key) {
|
|
3473
|
+
if (key.length <= 4)
|
|
3474
|
+
return "****";
|
|
3475
|
+
return "*".repeat(key.length - 4) + key.slice(-4);
|
|
3476
|
+
}
|
|
3477
|
+
async function setupCommand() {
|
|
3478
|
+
const rl = createInterface({ input, output });
|
|
3479
|
+
const projectRoot = process.cwd();
|
|
3480
|
+
try {
|
|
3481
|
+
printBanner();
|
|
3482
|
+
console.log(`${CYAN}Welcome to the Alfred setup wizard!${RESET}
|
|
3483
|
+
${DIM}This will walk you through configuring your AI assistant.${RESET}
|
|
3484
|
+
${DIM}Press Enter to accept defaults shown in [brackets].${RESET}
|
|
3485
|
+
`);
|
|
3486
|
+
const botName = await askWithDefault(rl, "What should your bot be called?", "Alfred");
|
|
3487
|
+
console.log(`
|
|
3488
|
+
${bold("Which LLM provider would you like to use?")}`);
|
|
3489
|
+
for (let i = 0; i < PROVIDERS.length; i++) {
|
|
3490
|
+
console.log(` ${cyan(String(i + 1) + ")")} ${PROVIDERS[i].label}`);
|
|
3491
|
+
}
|
|
3492
|
+
const providerChoice = await askNumber(rl, "> ", 1, PROVIDERS.length, 1);
|
|
3493
|
+
const provider = PROVIDERS[providerChoice - 1];
|
|
3494
|
+
console.log(` ${green(">")} Selected: ${bold(provider.label)}`);
|
|
3495
|
+
let apiKey = "";
|
|
3496
|
+
if (provider.needsApiKey) {
|
|
3497
|
+
console.log("");
|
|
3498
|
+
apiKey = await askRequired(rl, `Enter your ${provider.name.charAt(0).toUpperCase() + provider.name.slice(1)} API key`);
|
|
3499
|
+
console.log(` ${green(">")} API key set: ${dim(maskKey(apiKey))}`);
|
|
3500
|
+
} else {
|
|
3501
|
+
console.log(` ${dim("No API key needed for Ollama.")}`);
|
|
3502
|
+
}
|
|
3503
|
+
console.log("");
|
|
3504
|
+
const model = await askWithDefault(rl, "Which model?", provider.defaultModel);
|
|
3505
|
+
console.log(`
|
|
3506
|
+
${bold("Which messaging platforms do you want to enable?")}`);
|
|
3507
|
+
console.log(`${dim("(Enter comma-separated numbers, e.g. 1,3)")}`);
|
|
3508
|
+
for (let i = 0; i < PLATFORMS.length; i++) {
|
|
3509
|
+
console.log(` ${cyan(String(i + 1) + ")")} ${PLATFORMS[i].label}`);
|
|
3510
|
+
}
|
|
3511
|
+
console.log(` ${cyan("0)")} None (configure later)`);
|
|
3512
|
+
const platformInput = (await rl.question(`${YELLOW}> ${RESET}`)).trim();
|
|
3513
|
+
const selectedPlatforms = [];
|
|
3514
|
+
if (platformInput && platformInput !== "0") {
|
|
3515
|
+
const nums = platformInput.split(",").map((s) => parseInt(s.trim(), 10));
|
|
3516
|
+
for (const n of nums) {
|
|
3517
|
+
if (n >= 1 && n <= PLATFORMS.length) {
|
|
3518
|
+
const plat = PLATFORMS[n - 1];
|
|
3519
|
+
if (!selectedPlatforms.includes(plat)) {
|
|
3520
|
+
selectedPlatforms.push(plat);
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
if (selectedPlatforms.length > 0) {
|
|
3526
|
+
console.log(` ${green(">")} Enabling: ${selectedPlatforms.map((p) => bold(p.label)).join(", ")}`);
|
|
3527
|
+
} else {
|
|
3528
|
+
console.log(` ${dim("No platforms selected \u2014 you can configure them later.")}`);
|
|
3529
|
+
}
|
|
3530
|
+
const platformCredentials = {};
|
|
3531
|
+
const envOverrides = {};
|
|
3532
|
+
for (const platform of selectedPlatforms) {
|
|
3533
|
+
if (platform.credentials.length === 0) {
|
|
3534
|
+
if (platform.name === "whatsapp") {
|
|
3535
|
+
console.log(`
|
|
3536
|
+
${yellow("i")} WhatsApp: a QR code will be displayed on first start.`);
|
|
3537
|
+
}
|
|
3538
|
+
continue;
|
|
3539
|
+
}
|
|
3540
|
+
console.log(`
|
|
3541
|
+
${bold(platform.label + " configuration:")}`);
|
|
3542
|
+
const creds = {};
|
|
3543
|
+
for (const cred of platform.credentials) {
|
|
3544
|
+
let value;
|
|
3545
|
+
if (cred.defaultValue) {
|
|
3546
|
+
value = await askWithDefault(rl, ` ${cred.prompt}`, cred.defaultValue);
|
|
3547
|
+
} else if (cred.required) {
|
|
3548
|
+
value = await askRequired(rl, ` ${cred.prompt}`);
|
|
3549
|
+
} else {
|
|
3550
|
+
value = (await rl.question(` ${cred.prompt}: ${YELLOW}`)).trim();
|
|
3551
|
+
process.stdout.write(RESET);
|
|
3552
|
+
}
|
|
3553
|
+
creds[cred.configField] = value;
|
|
3554
|
+
envOverrides[cred.envKey] = value;
|
|
3555
|
+
if (cred.configField === "token" || cred.configField === "accessToken") {
|
|
3556
|
+
console.log(` ${green(">")} Set: ${dim(maskKey(value))}`);
|
|
3557
|
+
} else {
|
|
3558
|
+
console.log(` ${green(">")} Set: ${dim(value)}`);
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
platformCredentials[platform.configKey] = creds;
|
|
3562
|
+
}
|
|
3563
|
+
console.log("");
|
|
3564
|
+
const ownerUserIdInput = (await rl.question(`${BOLD}Owner user ID${RESET} ${dim("(optional, for elevated permissions)")}: ${YELLOW}`)).trim();
|
|
3565
|
+
process.stdout.write(RESET);
|
|
3566
|
+
const ownerUserId = ownerUserIdInput || "";
|
|
3567
|
+
console.log(`
|
|
3568
|
+
${bold("Writing configuration files...")}`);
|
|
3569
|
+
const envLines = [
|
|
3570
|
+
"# Alfred Environment Variables",
|
|
3571
|
+
"# Generated by `alfred setup`",
|
|
3572
|
+
"",
|
|
3573
|
+
"# === LLM ===",
|
|
3574
|
+
"",
|
|
3575
|
+
`ALFRED_LLM_PROVIDER=${provider.name}`
|
|
3576
|
+
];
|
|
3577
|
+
if (apiKey) {
|
|
3578
|
+
envLines.push(`${provider.envKeyName}=${apiKey}`);
|
|
3579
|
+
}
|
|
3580
|
+
if (model !== provider.defaultModel) {
|
|
3581
|
+
envLines.push(`ALFRED_LLM_MODEL=${model}`);
|
|
3582
|
+
}
|
|
3583
|
+
if (provider.baseUrl) {
|
|
3584
|
+
envLines.push(`ALFRED_LLM_BASE_URL=${provider.baseUrl}`);
|
|
3585
|
+
}
|
|
3586
|
+
envLines.push("", "# === Messaging Platforms ===", "");
|
|
3587
|
+
for (const [envKey, envVal] of Object.entries(envOverrides)) {
|
|
3588
|
+
envLines.push(`${envKey}=${envVal}`);
|
|
3589
|
+
}
|
|
3590
|
+
envLines.push("", "# === Security ===", "");
|
|
3591
|
+
if (ownerUserId) {
|
|
3592
|
+
envLines.push(`ALFRED_OWNER_USER_ID=${ownerUserId}`);
|
|
3593
|
+
} else {
|
|
3594
|
+
envLines.push("# ALFRED_OWNER_USER_ID=");
|
|
3595
|
+
}
|
|
3596
|
+
envLines.push("");
|
|
3597
|
+
const envPath = path4.join(projectRoot, ".env");
|
|
3598
|
+
fs4.writeFileSync(envPath, envLines.join("\n"), "utf-8");
|
|
3599
|
+
console.log(` ${green("+")} ${dim(".env")} written`);
|
|
3600
|
+
const configDir = path4.join(projectRoot, "config");
|
|
3601
|
+
if (!fs4.existsSync(configDir)) {
|
|
3602
|
+
fs4.mkdirSync(configDir, { recursive: true });
|
|
3603
|
+
}
|
|
3604
|
+
const config = {
|
|
3605
|
+
name: botName,
|
|
3606
|
+
telegram: {
|
|
3607
|
+
token: platformCredentials["telegram"]?.["token"] ?? "",
|
|
3608
|
+
enabled: selectedPlatforms.some((p) => p.name === "telegram")
|
|
3609
|
+
},
|
|
3610
|
+
discord: {
|
|
3611
|
+
token: platformCredentials["discord"]?.["token"] ?? "",
|
|
3612
|
+
enabled: selectedPlatforms.some((p) => p.name === "discord")
|
|
3613
|
+
},
|
|
3614
|
+
whatsapp: {
|
|
3615
|
+
enabled: selectedPlatforms.some((p) => p.name === "whatsapp"),
|
|
3616
|
+
dataPath: "./data/whatsapp"
|
|
3617
|
+
},
|
|
3618
|
+
matrix: {
|
|
3619
|
+
homeserverUrl: platformCredentials["matrix"]?.["homeserverUrl"] ?? "https://matrix.org",
|
|
3620
|
+
accessToken: platformCredentials["matrix"]?.["accessToken"] ?? "",
|
|
3621
|
+
userId: platformCredentials["matrix"]?.["userId"] ?? "",
|
|
3622
|
+
enabled: selectedPlatforms.some((p) => p.name === "matrix")
|
|
3623
|
+
},
|
|
3624
|
+
signal: {
|
|
3625
|
+
apiUrl: platformCredentials["signal"]?.["apiUrl"] ?? "http://localhost:8080",
|
|
3626
|
+
phoneNumber: platformCredentials["signal"]?.["phoneNumber"] ?? "",
|
|
3627
|
+
enabled: selectedPlatforms.some((p) => p.name === "signal")
|
|
3628
|
+
},
|
|
3629
|
+
llm: {
|
|
3630
|
+
provider: provider.name,
|
|
3631
|
+
model,
|
|
3632
|
+
temperature: 0.7,
|
|
3633
|
+
maxTokens: 4096
|
|
3634
|
+
},
|
|
3635
|
+
storage: {
|
|
3636
|
+
path: "./data/alfred.db"
|
|
3637
|
+
},
|
|
3638
|
+
logger: {
|
|
3639
|
+
level: "info",
|
|
3640
|
+
pretty: true,
|
|
3641
|
+
auditLogPath: "./data/audit.log"
|
|
3642
|
+
},
|
|
3643
|
+
security: {
|
|
3644
|
+
rulesPath: "./config/rules",
|
|
3645
|
+
defaultEffect: "deny"
|
|
3646
|
+
}
|
|
3647
|
+
};
|
|
3648
|
+
if (ownerUserId) {
|
|
3649
|
+
config.security.ownerUserId = ownerUserId;
|
|
3650
|
+
}
|
|
3651
|
+
const yamlStr = "# Alfred \u2014 Configuration\n# Generated by `alfred setup`\n# Edit manually or re-run `alfred setup` to reconfigure.\n\n" + yaml2.dump(config, { lineWidth: 120, noRefs: true, sortKeys: false });
|
|
3652
|
+
const configPath = path4.join(configDir, "default.yml");
|
|
3653
|
+
fs4.writeFileSync(configPath, yamlStr, "utf-8");
|
|
3654
|
+
console.log(` ${green("+")} ${dim("config/default.yml")} written`);
|
|
3655
|
+
const dataDir = path4.join(projectRoot, "data");
|
|
3656
|
+
if (!fs4.existsSync(dataDir)) {
|
|
3657
|
+
fs4.mkdirSync(dataDir, { recursive: true });
|
|
3658
|
+
console.log(` ${green("+")} ${dim("data/")} directory created`);
|
|
3659
|
+
}
|
|
3660
|
+
console.log("");
|
|
3661
|
+
console.log(`${GREEN}${"=".repeat(52)}${RESET}`);
|
|
3662
|
+
console.log(`${GREEN}${BOLD} Setup complete!${RESET}`);
|
|
3663
|
+
console.log(`${GREEN}${"=".repeat(52)}${RESET}`);
|
|
3664
|
+
console.log("");
|
|
3665
|
+
console.log(` ${bold("Bot name:")} ${botName}`);
|
|
3666
|
+
console.log(` ${bold("LLM provider:")} ${provider.name} (${model})`);
|
|
3667
|
+
if (apiKey) {
|
|
3668
|
+
console.log(` ${bold("API key:")} ${maskKey(apiKey)}`);
|
|
3669
|
+
}
|
|
3670
|
+
if (selectedPlatforms.length > 0) {
|
|
3671
|
+
console.log(` ${bold("Platforms:")} ${selectedPlatforms.map((p) => p.label).join(", ")}`);
|
|
3672
|
+
} else {
|
|
3673
|
+
console.log(` ${bold("Platforms:")} none (configure later)`);
|
|
3674
|
+
}
|
|
3675
|
+
if (ownerUserId) {
|
|
3676
|
+
console.log(` ${bold("Owner ID:")} ${ownerUserId}`);
|
|
3677
|
+
}
|
|
3678
|
+
console.log("");
|
|
3679
|
+
console.log(`${CYAN}Next steps:${RESET}`);
|
|
3680
|
+
console.log(` ${bold("alfred start")} Start Alfred`);
|
|
3681
|
+
console.log(` ${bold("alfred status")} Check configuration`);
|
|
3682
|
+
console.log(` ${bold("alfred --help")} Show all commands`);
|
|
3683
|
+
console.log("");
|
|
3684
|
+
console.log(`${DIM}Edit ${bold(".env")}${DIM} or ${bold("config/default.yml")}${DIM} for manual configuration.${RESET}`);
|
|
3685
|
+
console.log("");
|
|
3686
|
+
} finally {
|
|
3687
|
+
rl.close();
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
async function askWithDefault(rl, prompt, defaultValue) {
|
|
3691
|
+
const answer = (await rl.question(`${BOLD}${prompt}${RESET} ${dim(`[${defaultValue}]`)}: ${YELLOW}`)).trim();
|
|
3692
|
+
process.stdout.write(RESET);
|
|
3693
|
+
return answer || defaultValue;
|
|
3694
|
+
}
|
|
3695
|
+
async function askRequired(rl, prompt) {
|
|
3696
|
+
while (true) {
|
|
3697
|
+
const answer = (await rl.question(`${BOLD}${prompt}${RESET}: ${YELLOW}`)).trim();
|
|
3698
|
+
process.stdout.write(RESET);
|
|
3699
|
+
if (answer)
|
|
3700
|
+
return answer;
|
|
3701
|
+
console.log(` ${red("!")} This field is required. Please enter a value.`);
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
async function askNumber(rl, prompt, min, max, defaultValue) {
|
|
3705
|
+
while (true) {
|
|
3706
|
+
const answer = (await rl.question(`${YELLOW}${prompt}${RESET}`)).trim();
|
|
3707
|
+
if (!answer)
|
|
3708
|
+
return defaultValue;
|
|
3709
|
+
const n = parseInt(answer, 10);
|
|
3710
|
+
if (!Number.isNaN(n) && n >= min && n <= max)
|
|
3711
|
+
return n;
|
|
3712
|
+
console.log(` ${red("!")} Please enter a number between ${min} and ${max}.`);
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
function printBanner() {
|
|
3716
|
+
console.log(`
|
|
3717
|
+
${MAGENTA}${BOLD} _ _ _____ ____ _____ ____
|
|
3718
|
+
/ \\ | | | ___| _ \\| ____| _ \\
|
|
3719
|
+
/ _ \\ | | | |_ | |_) | _| | | | |
|
|
3720
|
+
/ ___ \\| |___| _| | _ <| |___| |_| |
|
|
3721
|
+
/_/ \\_\\_____|_| |_| \\_\\_____|____/ ${RESET}
|
|
3722
|
+
${DIM} Personal AI Assistant \u2014 Setup Wizard${RESET}
|
|
3723
|
+
`);
|
|
3724
|
+
}
|
|
3725
|
+
var RESET, BOLD, DIM, GREEN, YELLOW, CYAN, RED, MAGENTA, PROVIDERS, PLATFORMS;
|
|
3726
|
+
var init_setup = __esm({
|
|
3727
|
+
"dist/commands/setup.js"() {
|
|
3728
|
+
"use strict";
|
|
3729
|
+
RESET = "\x1B[0m";
|
|
3730
|
+
BOLD = "\x1B[1m";
|
|
3731
|
+
DIM = "\x1B[2m";
|
|
3732
|
+
GREEN = "\x1B[32m";
|
|
3733
|
+
YELLOW = "\x1B[33m";
|
|
3734
|
+
CYAN = "\x1B[36m";
|
|
3735
|
+
RED = "\x1B[31m";
|
|
3736
|
+
MAGENTA = "\x1B[35m";
|
|
3737
|
+
PROVIDERS = [
|
|
3738
|
+
{
|
|
3739
|
+
name: "anthropic",
|
|
3740
|
+
label: "Anthropic (Claude) \u2014 recommended",
|
|
3741
|
+
defaultModel: "claude-sonnet-4-20250514",
|
|
3742
|
+
envKeyName: "ALFRED_ANTHROPIC_API_KEY",
|
|
3743
|
+
needsApiKey: true
|
|
3744
|
+
},
|
|
3745
|
+
{
|
|
3746
|
+
name: "openai",
|
|
3747
|
+
label: "OpenAI (GPT)",
|
|
3748
|
+
defaultModel: "gpt-4o",
|
|
3749
|
+
envKeyName: "ALFRED_OPENAI_API_KEY",
|
|
3750
|
+
needsApiKey: true
|
|
3751
|
+
},
|
|
3752
|
+
{
|
|
3753
|
+
name: "openrouter",
|
|
3754
|
+
label: "OpenRouter (multiple providers)",
|
|
3755
|
+
defaultModel: "anthropic/claude-sonnet-4-20250514",
|
|
3756
|
+
envKeyName: "ALFRED_OPENROUTER_API_KEY",
|
|
3757
|
+
needsApiKey: true,
|
|
3758
|
+
baseUrl: "https://openrouter.ai/api/v1"
|
|
3759
|
+
},
|
|
3760
|
+
{
|
|
3761
|
+
name: "ollama",
|
|
3762
|
+
label: "Ollama (local, no API key needed)",
|
|
3763
|
+
defaultModel: "llama3.2",
|
|
3764
|
+
envKeyName: "",
|
|
3765
|
+
needsApiKey: false,
|
|
3766
|
+
baseUrl: "http://localhost:11434/v1"
|
|
3767
|
+
}
|
|
3768
|
+
];
|
|
3769
|
+
PLATFORMS = [
|
|
3770
|
+
{
|
|
3771
|
+
name: "telegram",
|
|
3772
|
+
label: "Telegram",
|
|
3773
|
+
configKey: "telegram",
|
|
3774
|
+
credentials: [
|
|
3775
|
+
{
|
|
3776
|
+
envKey: "ALFRED_TELEGRAM_TOKEN",
|
|
3777
|
+
configField: "token",
|
|
3778
|
+
prompt: "Enter your Telegram Bot token (from @BotFather)",
|
|
3779
|
+
required: true
|
|
3780
|
+
}
|
|
3781
|
+
]
|
|
3782
|
+
},
|
|
3783
|
+
{
|
|
3784
|
+
name: "discord",
|
|
3785
|
+
label: "Discord",
|
|
3786
|
+
configKey: "discord",
|
|
3787
|
+
credentials: [
|
|
3788
|
+
{
|
|
3789
|
+
envKey: "ALFRED_DISCORD_TOKEN",
|
|
3790
|
+
configField: "token",
|
|
3791
|
+
prompt: "Enter your Discord Bot token",
|
|
3792
|
+
required: true
|
|
3793
|
+
}
|
|
3794
|
+
]
|
|
3795
|
+
},
|
|
3796
|
+
{
|
|
3797
|
+
name: "whatsapp",
|
|
3798
|
+
label: "WhatsApp",
|
|
3799
|
+
configKey: "whatsapp",
|
|
3800
|
+
credentials: []
|
|
3801
|
+
},
|
|
3802
|
+
{
|
|
3803
|
+
name: "matrix",
|
|
3804
|
+
label: "Matrix",
|
|
3805
|
+
configKey: "matrix",
|
|
3806
|
+
credentials: [
|
|
3807
|
+
{
|
|
3808
|
+
envKey: "ALFRED_MATRIX_HOMESERVER_URL",
|
|
3809
|
+
configField: "homeserverUrl",
|
|
3810
|
+
prompt: "Enter your Matrix homeserver URL",
|
|
3811
|
+
defaultValue: "https://matrix.org",
|
|
3812
|
+
required: true
|
|
3813
|
+
},
|
|
3814
|
+
{
|
|
3815
|
+
envKey: "ALFRED_MATRIX_ACCESS_TOKEN",
|
|
3816
|
+
configField: "accessToken",
|
|
3817
|
+
prompt: "Enter your Matrix access token",
|
|
3818
|
+
required: true
|
|
3819
|
+
},
|
|
3820
|
+
{
|
|
3821
|
+
envKey: "ALFRED_MATRIX_USER_ID",
|
|
3822
|
+
configField: "userId",
|
|
3823
|
+
prompt: "Enter your Matrix user ID (e.g. @bot:matrix.org)",
|
|
3824
|
+
required: true
|
|
3825
|
+
}
|
|
3826
|
+
]
|
|
3827
|
+
},
|
|
3828
|
+
{
|
|
3829
|
+
name: "signal",
|
|
3830
|
+
label: "Signal",
|
|
3831
|
+
configKey: "signal",
|
|
3832
|
+
credentials: [
|
|
3833
|
+
{
|
|
3834
|
+
envKey: "ALFRED_SIGNAL_API_URL",
|
|
3835
|
+
configField: "apiUrl",
|
|
3836
|
+
prompt: "Enter the Signal REST API URL",
|
|
3837
|
+
defaultValue: "http://localhost:8080",
|
|
3838
|
+
required: true
|
|
3839
|
+
},
|
|
3840
|
+
{
|
|
3841
|
+
envKey: "ALFRED_SIGNAL_PHONE_NUMBER",
|
|
3842
|
+
configField: "phoneNumber",
|
|
3843
|
+
prompt: "Enter the Signal phone number (e.g. +15551234567)",
|
|
3844
|
+
required: true
|
|
3845
|
+
}
|
|
3846
|
+
]
|
|
3847
|
+
}
|
|
3848
|
+
];
|
|
3849
|
+
}
|
|
3850
|
+
});
|
|
3851
|
+
|
|
3852
|
+
// dist/commands/config.js
|
|
3853
|
+
var config_exports = {};
|
|
3854
|
+
__export(config_exports, {
|
|
3855
|
+
configCommand: () => configCommand
|
|
3856
|
+
});
|
|
3857
|
+
function isSensitiveKey(key) {
|
|
3858
|
+
const lower = key.toLowerCase();
|
|
3859
|
+
return SENSITIVE_KEYS.some((s) => lower.includes(s));
|
|
3860
|
+
}
|
|
3861
|
+
function redactValue(value) {
|
|
3862
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
3863
|
+
return "(empty)";
|
|
3864
|
+
}
|
|
3865
|
+
if (value.length <= 8) {
|
|
3866
|
+
return "***";
|
|
3867
|
+
}
|
|
3868
|
+
return value.slice(0, 4) + "..." + value.slice(-4);
|
|
3869
|
+
}
|
|
3870
|
+
function redactObject(obj) {
|
|
3871
|
+
const result = {};
|
|
3872
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
3873
|
+
if (isSensitiveKey(key)) {
|
|
3874
|
+
result[key] = redactValue(value);
|
|
3875
|
+
} else if (value !== null && value !== void 0 && typeof value === "object" && !Array.isArray(value)) {
|
|
3876
|
+
result[key] = redactObject(value);
|
|
3877
|
+
} else {
|
|
3878
|
+
result[key] = value;
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
return result;
|
|
3882
|
+
}
|
|
3883
|
+
async function configCommand() {
|
|
3884
|
+
const configLoader = new ConfigLoader();
|
|
3885
|
+
let config;
|
|
3886
|
+
try {
|
|
3887
|
+
config = configLoader.loadConfig();
|
|
3888
|
+
} catch (error) {
|
|
3889
|
+
console.error("Failed to load configuration:", error.message);
|
|
3890
|
+
process.exit(1);
|
|
3891
|
+
}
|
|
3892
|
+
const redacted = redactObject(config);
|
|
3893
|
+
console.log("Alfred \u2014 Resolved Configuration");
|
|
3894
|
+
console.log("================================");
|
|
3895
|
+
console.log(JSON.stringify(redacted, null, 2));
|
|
3896
|
+
}
|
|
3897
|
+
var SENSITIVE_KEYS;
|
|
3898
|
+
var init_config = __esm({
|
|
3899
|
+
"dist/commands/config.js"() {
|
|
3900
|
+
"use strict";
|
|
3901
|
+
init_dist();
|
|
3902
|
+
SENSITIVE_KEYS = ["token", "apikey", "api_key", "accesstoken", "secret", "password"];
|
|
3903
|
+
}
|
|
3904
|
+
});
|
|
3905
|
+
|
|
3906
|
+
// dist/commands/rules.js
|
|
3907
|
+
var rules_exports = {};
|
|
3908
|
+
__export(rules_exports, {
|
|
3909
|
+
rulesCommand: () => rulesCommand
|
|
3910
|
+
});
|
|
3911
|
+
import fs5 from "node:fs";
|
|
3912
|
+
import path5 from "node:path";
|
|
3913
|
+
import yaml3 from "js-yaml";
|
|
3914
|
+
async function rulesCommand() {
|
|
3915
|
+
const configLoader = new ConfigLoader();
|
|
3916
|
+
let config;
|
|
3917
|
+
try {
|
|
3918
|
+
config = configLoader.loadConfig();
|
|
3919
|
+
} catch (error) {
|
|
3920
|
+
console.error("Failed to load configuration:", error.message);
|
|
3921
|
+
process.exit(1);
|
|
3922
|
+
}
|
|
3923
|
+
const rulesPath = path5.resolve(config.security.rulesPath);
|
|
3924
|
+
if (!fs5.existsSync(rulesPath)) {
|
|
3925
|
+
console.log(`Rules directory not found: ${rulesPath}`);
|
|
3926
|
+
console.log("No security rules loaded.");
|
|
3927
|
+
return;
|
|
3928
|
+
}
|
|
3929
|
+
const stat = fs5.statSync(rulesPath);
|
|
3930
|
+
if (!stat.isDirectory()) {
|
|
3931
|
+
console.error(`Rules path is not a directory: ${rulesPath}`);
|
|
3932
|
+
process.exit(1);
|
|
3933
|
+
}
|
|
3934
|
+
const files = fs5.readdirSync(rulesPath).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
3935
|
+
if (files.length === 0) {
|
|
3936
|
+
console.log(`No YAML rule files found in: ${rulesPath}`);
|
|
3937
|
+
return;
|
|
3938
|
+
}
|
|
3939
|
+
const ruleLoader = new RuleLoader();
|
|
3940
|
+
const allRules = [];
|
|
3941
|
+
const errors = [];
|
|
3942
|
+
for (const file of files) {
|
|
3943
|
+
const filePath = path5.join(rulesPath, file);
|
|
3944
|
+
try {
|
|
3945
|
+
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
3946
|
+
const parsed = yaml3.load(raw);
|
|
3947
|
+
const rules = ruleLoader.loadFromObject(parsed);
|
|
3948
|
+
allRules.push(...rules);
|
|
3949
|
+
} catch (error) {
|
|
3950
|
+
errors.push(` ${file}: ${error.message}`);
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
console.log("Alfred \u2014 Security Rules");
|
|
3954
|
+
console.log("=======================");
|
|
3955
|
+
console.log(`Rules directory: ${rulesPath}`);
|
|
3956
|
+
console.log(`Rule files found: ${files.length}`);
|
|
3957
|
+
console.log(`Total rules loaded: ${allRules.length}`);
|
|
3958
|
+
console.log("");
|
|
3959
|
+
if (errors.length > 0) {
|
|
3960
|
+
console.log("Errors:");
|
|
3961
|
+
for (const err of errors) {
|
|
3962
|
+
console.log(err);
|
|
3963
|
+
}
|
|
3964
|
+
console.log("");
|
|
3965
|
+
}
|
|
3966
|
+
if (allRules.length === 0) {
|
|
3967
|
+
return;
|
|
3968
|
+
}
|
|
3969
|
+
allRules.sort((a, b) => a.priority - b.priority);
|
|
3970
|
+
console.log("Loaded rules (sorted by priority):");
|
|
3971
|
+
console.log("");
|
|
3972
|
+
for (const rule of allRules) {
|
|
3973
|
+
const rateLimit = rule.rateLimit ? ` | rate-limit: ${rule.rateLimit.maxInvocations}/${rule.rateLimit.windowSeconds}s` : "";
|
|
3974
|
+
console.log(` [${rule.priority}] ${rule.id}`);
|
|
3975
|
+
console.log(` effect: ${rule.effect} | scope: ${rule.scope}`);
|
|
3976
|
+
console.log(` actions: ${rule.actions.join(", ")}`);
|
|
3977
|
+
console.log(` risk levels: ${rule.riskLevels.join(", ")}${rateLimit}`);
|
|
3978
|
+
if (rule.conditions) {
|
|
3979
|
+
console.log(` conditions: ${JSON.stringify(rule.conditions)}`);
|
|
3980
|
+
}
|
|
3981
|
+
console.log("");
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
var init_rules = __esm({
|
|
3985
|
+
"dist/commands/rules.js"() {
|
|
3986
|
+
"use strict";
|
|
3987
|
+
init_dist();
|
|
3988
|
+
init_dist5();
|
|
3989
|
+
}
|
|
3990
|
+
});
|
|
3991
|
+
|
|
3992
|
+
// dist/commands/status.js
|
|
3993
|
+
var status_exports = {};
|
|
3994
|
+
__export(status_exports, {
|
|
3995
|
+
statusCommand: () => statusCommand
|
|
3996
|
+
});
|
|
3997
|
+
import fs6 from "node:fs";
|
|
3998
|
+
import path6 from "node:path";
|
|
3999
|
+
import yaml4 from "js-yaml";
|
|
4000
|
+
async function statusCommand() {
|
|
4001
|
+
const configLoader = new ConfigLoader();
|
|
4002
|
+
let config;
|
|
4003
|
+
try {
|
|
4004
|
+
config = configLoader.loadConfig();
|
|
4005
|
+
} catch (error) {
|
|
4006
|
+
console.error("Failed to load configuration:", error.message);
|
|
4007
|
+
process.exit(1);
|
|
4008
|
+
}
|
|
4009
|
+
console.log("Alfred \u2014 Status");
|
|
4010
|
+
console.log("================");
|
|
4011
|
+
console.log("");
|
|
4012
|
+
const adapters = [
|
|
4013
|
+
{
|
|
4014
|
+
name: "Telegram",
|
|
4015
|
+
enabled: config.telegram.enabled,
|
|
4016
|
+
configured: Boolean(config.telegram.token)
|
|
4017
|
+
},
|
|
4018
|
+
{
|
|
4019
|
+
name: "Discord",
|
|
4020
|
+
enabled: Boolean(config.discord?.enabled),
|
|
4021
|
+
configured: Boolean(config.discord?.token)
|
|
4022
|
+
},
|
|
4023
|
+
{
|
|
4024
|
+
name: "WhatsApp",
|
|
4025
|
+
enabled: Boolean(config.whatsapp?.enabled),
|
|
4026
|
+
configured: Boolean(config.whatsapp?.dataPath)
|
|
4027
|
+
},
|
|
4028
|
+
{
|
|
4029
|
+
name: "Matrix",
|
|
4030
|
+
enabled: Boolean(config.matrix?.enabled),
|
|
4031
|
+
configured: Boolean(config.matrix?.accessToken)
|
|
4032
|
+
},
|
|
4033
|
+
{
|
|
4034
|
+
name: "Signal",
|
|
4035
|
+
enabled: Boolean(config.signal?.enabled),
|
|
4036
|
+
configured: Boolean(config.signal?.phoneNumber)
|
|
4037
|
+
}
|
|
4038
|
+
];
|
|
4039
|
+
console.log("Messaging Adapters:");
|
|
4040
|
+
for (const adapter of adapters) {
|
|
4041
|
+
const status = adapter.enabled ? "enabled" : adapter.configured ? "configured (disabled)" : "not configured";
|
|
4042
|
+
const icon = adapter.enabled ? "+" : "-";
|
|
4043
|
+
console.log(` [${icon}] ${adapter.name}: ${status}`);
|
|
4044
|
+
}
|
|
4045
|
+
console.log("");
|
|
4046
|
+
console.log("LLM Provider:");
|
|
4047
|
+
console.log(` Provider: ${config.llm.provider}`);
|
|
4048
|
+
console.log(` Model: ${config.llm.model}`);
|
|
4049
|
+
console.log(` API Key: ${config.llm.apiKey ? "set" : "not set"}`);
|
|
4050
|
+
if (config.llm.baseUrl) {
|
|
4051
|
+
console.log(` Base URL: ${config.llm.baseUrl}`);
|
|
4052
|
+
}
|
|
4053
|
+
console.log("");
|
|
4054
|
+
console.log("Storage:");
|
|
4055
|
+
const dbPath = path6.resolve(config.storage.path);
|
|
4056
|
+
const dbExists = fs6.existsSync(dbPath);
|
|
4057
|
+
console.log(` Database: ${dbPath}`);
|
|
4058
|
+
console.log(` Status: ${dbExists ? "exists" : "not yet created"}`);
|
|
4059
|
+
console.log("");
|
|
4060
|
+
const rulesPath = path6.resolve(config.security.rulesPath);
|
|
4061
|
+
let ruleCount = 0;
|
|
4062
|
+
let ruleFileCount = 0;
|
|
4063
|
+
if (fs6.existsSync(rulesPath) && fs6.statSync(rulesPath).isDirectory()) {
|
|
4064
|
+
const files = fs6.readdirSync(rulesPath).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
4065
|
+
ruleFileCount = files.length;
|
|
4066
|
+
const ruleLoader = new RuleLoader();
|
|
4067
|
+
for (const file of files) {
|
|
4068
|
+
const filePath = path6.join(rulesPath, file);
|
|
4069
|
+
try {
|
|
4070
|
+
const raw = fs6.readFileSync(filePath, "utf-8");
|
|
4071
|
+
const parsed = yaml4.load(raw);
|
|
4072
|
+
const rules = ruleLoader.loadFromObject(parsed);
|
|
4073
|
+
ruleCount += rules.length;
|
|
4074
|
+
} catch {
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
4078
|
+
console.log("Security:");
|
|
4079
|
+
console.log(` Rules path: ${rulesPath}`);
|
|
4080
|
+
console.log(` Rule files: ${ruleFileCount}`);
|
|
4081
|
+
console.log(` Rules loaded: ${ruleCount}`);
|
|
4082
|
+
console.log(` Default effect: ${config.security.defaultEffect}`);
|
|
4083
|
+
if (config.security.ownerUserId) {
|
|
4084
|
+
console.log(` Owner user ID: ${config.security.ownerUserId}`);
|
|
4085
|
+
}
|
|
4086
|
+
console.log("");
|
|
4087
|
+
console.log("Logger:");
|
|
4088
|
+
console.log(` Level: ${config.logger.level}`);
|
|
4089
|
+
console.log(` Pretty: ${config.logger.pretty}`);
|
|
4090
|
+
}
|
|
4091
|
+
var init_status = __esm({
|
|
4092
|
+
"dist/commands/status.js"() {
|
|
4093
|
+
"use strict";
|
|
4094
|
+
init_dist();
|
|
4095
|
+
init_dist5();
|
|
4096
|
+
}
|
|
4097
|
+
});
|
|
4098
|
+
|
|
4099
|
+
// dist/commands/logs.js
|
|
4100
|
+
var logs_exports = {};
|
|
4101
|
+
__export(logs_exports, {
|
|
4102
|
+
logsCommand: () => logsCommand
|
|
4103
|
+
});
|
|
4104
|
+
import fs7 from "node:fs";
|
|
4105
|
+
import path7 from "node:path";
|
|
4106
|
+
async function logsCommand(tail) {
|
|
4107
|
+
const configLoader = new ConfigLoader();
|
|
4108
|
+
let config;
|
|
4109
|
+
try {
|
|
4110
|
+
config = configLoader.loadConfig();
|
|
4111
|
+
} catch (error) {
|
|
4112
|
+
console.error("Failed to load configuration:", error.message);
|
|
4113
|
+
process.exit(1);
|
|
4114
|
+
}
|
|
4115
|
+
const dbPath = path7.resolve(config.storage.path);
|
|
4116
|
+
if (!fs7.existsSync(dbPath)) {
|
|
4117
|
+
console.log(`Database not found at: ${dbPath}`);
|
|
4118
|
+
console.log("No audit log entries. Alfred has not been run yet, or the database path is incorrect.");
|
|
4119
|
+
return;
|
|
4120
|
+
}
|
|
4121
|
+
let database;
|
|
4122
|
+
try {
|
|
4123
|
+
database = new Database(dbPath);
|
|
4124
|
+
const auditRepo = new AuditRepository(database.getDb());
|
|
4125
|
+
const totalCount = auditRepo.count({});
|
|
4126
|
+
const entries = auditRepo.query({ limit: tail });
|
|
4127
|
+
console.log("Alfred \u2014 Audit Log");
|
|
4128
|
+
console.log("===================");
|
|
4129
|
+
console.log(`Total entries: ${totalCount}`);
|
|
4130
|
+
console.log(`Showing last ${Math.min(tail, totalCount)} entries:`);
|
|
4131
|
+
console.log("");
|
|
4132
|
+
if (entries.length === 0) {
|
|
4133
|
+
console.log("No audit log entries found.");
|
|
4134
|
+
return;
|
|
4135
|
+
}
|
|
4136
|
+
for (const entry of entries) {
|
|
4137
|
+
const timestamp = entry.timestamp.toISOString();
|
|
4138
|
+
const effect = entry.effect === "allow" ? "ALLOW" : "DENY ";
|
|
4139
|
+
console.log(` ${timestamp} [${effect}] ${entry.action}`);
|
|
4140
|
+
console.log(` user: ${entry.userId} | platform: ${entry.platform} | risk: ${entry.riskLevel}`);
|
|
4141
|
+
if (entry.ruleId) {
|
|
4142
|
+
console.log(` rule: ${entry.ruleId}`);
|
|
4143
|
+
}
|
|
4144
|
+
if (entry.chatId) {
|
|
4145
|
+
console.log(` chat: ${entry.chatId}`);
|
|
4146
|
+
}
|
|
4147
|
+
if (entry.context) {
|
|
4148
|
+
console.log(` context: ${JSON.stringify(entry.context)}`);
|
|
4149
|
+
}
|
|
4150
|
+
console.log("");
|
|
4151
|
+
}
|
|
4152
|
+
} catch (error) {
|
|
4153
|
+
console.error("Failed to read audit log:", error.message);
|
|
4154
|
+
process.exit(1);
|
|
4155
|
+
} finally {
|
|
4156
|
+
if (database) {
|
|
4157
|
+
database.close();
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
var init_logs = __esm({
|
|
4162
|
+
"dist/commands/logs.js"() {
|
|
4163
|
+
"use strict";
|
|
4164
|
+
init_dist();
|
|
4165
|
+
init_dist3();
|
|
4166
|
+
}
|
|
4167
|
+
});
|
|
4168
|
+
|
|
4169
|
+
// dist/index.js
|
|
4170
|
+
var VERSION = "0.1.0";
|
|
4171
|
+
var HELP_TEXT = `
|
|
4172
|
+
Alfred CLI v${VERSION}
|
|
4173
|
+
Personal AI Assistant
|
|
4174
|
+
|
|
4175
|
+
Usage:
|
|
4176
|
+
alfred <command> [options]
|
|
4177
|
+
|
|
4178
|
+
Commands:
|
|
4179
|
+
start Start Alfred (load config, bootstrap, and run)
|
|
4180
|
+
setup Interactive setup wizard (configure LLM, platforms, API keys)
|
|
4181
|
+
config Show current resolved configuration (API keys redacted)
|
|
4182
|
+
rules List loaded security rules from the rules path
|
|
4183
|
+
status Show status overview (adapters, LLM, rules)
|
|
4184
|
+
logs [--tail N] Show recent audit log entries (default: 20)
|
|
4185
|
+
|
|
4186
|
+
Options:
|
|
4187
|
+
--help, -h Show this help message
|
|
4188
|
+
--version, -v Show version number
|
|
4189
|
+
`.trim();
|
|
4190
|
+
function parseArgs(argv) {
|
|
4191
|
+
const args = argv.slice(2);
|
|
4192
|
+
const command = args.length > 0 && !args[0].startsWith("-") ? args[0] : "";
|
|
4193
|
+
const remaining = command ? args.slice(1) : args;
|
|
4194
|
+
const flags = {};
|
|
4195
|
+
const positional = [];
|
|
4196
|
+
let i = 0;
|
|
4197
|
+
while (i < remaining.length) {
|
|
4198
|
+
const arg = remaining[i];
|
|
4199
|
+
if (arg.startsWith("--")) {
|
|
4200
|
+
const key = arg.slice(2);
|
|
4201
|
+
if (i + 1 < remaining.length && !remaining[i + 1].startsWith("-")) {
|
|
4202
|
+
flags[key] = remaining[i + 1];
|
|
4203
|
+
i += 2;
|
|
4204
|
+
} else {
|
|
4205
|
+
flags[key] = true;
|
|
4206
|
+
i += 1;
|
|
4207
|
+
}
|
|
4208
|
+
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
4209
|
+
const key = arg.slice(1);
|
|
4210
|
+
if (i + 1 < remaining.length && !remaining[i + 1].startsWith("-")) {
|
|
4211
|
+
flags[key] = remaining[i + 1];
|
|
4212
|
+
i += 2;
|
|
4213
|
+
} else {
|
|
4214
|
+
flags[key] = true;
|
|
4215
|
+
i += 1;
|
|
4216
|
+
}
|
|
4217
|
+
} else {
|
|
4218
|
+
positional.push(arg);
|
|
4219
|
+
i += 1;
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
return { command, flags, positional };
|
|
4223
|
+
}
|
|
4224
|
+
async function main() {
|
|
4225
|
+
const parsed = parseArgs(process.argv);
|
|
4226
|
+
if (parsed.flags["help"] || parsed.flags["h"]) {
|
|
4227
|
+
console.log(HELP_TEXT);
|
|
4228
|
+
process.exit(0);
|
|
4229
|
+
}
|
|
4230
|
+
if (parsed.flags["version"] || parsed.flags["v"]) {
|
|
4231
|
+
console.log(`alfred v${VERSION}`);
|
|
4232
|
+
process.exit(0);
|
|
4233
|
+
}
|
|
4234
|
+
switch (parsed.command) {
|
|
4235
|
+
case "start": {
|
|
4236
|
+
const { startCommand: startCommand2 } = await Promise.resolve().then(() => (init_start(), start_exports));
|
|
4237
|
+
await startCommand2();
|
|
4238
|
+
break;
|
|
4239
|
+
}
|
|
4240
|
+
case "setup": {
|
|
4241
|
+
const { setupCommand: setupCommand2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
4242
|
+
await setupCommand2();
|
|
4243
|
+
break;
|
|
4244
|
+
}
|
|
4245
|
+
case "config": {
|
|
4246
|
+
const { configCommand: configCommand2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4247
|
+
await configCommand2();
|
|
4248
|
+
break;
|
|
4249
|
+
}
|
|
4250
|
+
case "rules": {
|
|
4251
|
+
const { rulesCommand: rulesCommand2 } = await Promise.resolve().then(() => (init_rules(), rules_exports));
|
|
4252
|
+
await rulesCommand2();
|
|
4253
|
+
break;
|
|
4254
|
+
}
|
|
4255
|
+
case "status": {
|
|
4256
|
+
const { statusCommand: statusCommand2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
4257
|
+
await statusCommand2();
|
|
4258
|
+
break;
|
|
4259
|
+
}
|
|
4260
|
+
case "logs": {
|
|
4261
|
+
const tailValue = parsed.flags["tail"];
|
|
4262
|
+
let tail = 20;
|
|
4263
|
+
if (typeof tailValue === "string") {
|
|
4264
|
+
const tailNum = parseInt(tailValue, 10);
|
|
4265
|
+
if (Number.isNaN(tailNum) || tailNum <= 0) {
|
|
4266
|
+
console.error("Error: --tail must be a positive integer");
|
|
4267
|
+
process.exit(1);
|
|
4268
|
+
}
|
|
4269
|
+
tail = tailNum;
|
|
4270
|
+
}
|
|
4271
|
+
const { logsCommand: logsCommand2 } = await Promise.resolve().then(() => (init_logs(), logs_exports));
|
|
4272
|
+
await logsCommand2(tail);
|
|
4273
|
+
break;
|
|
4274
|
+
}
|
|
4275
|
+
case "help":
|
|
4276
|
+
console.log(HELP_TEXT);
|
|
4277
|
+
break;
|
|
4278
|
+
case "":
|
|
4279
|
+
console.log(HELP_TEXT);
|
|
4280
|
+
process.exit(0);
|
|
4281
|
+
break;
|
|
4282
|
+
default:
|
|
4283
|
+
console.error(`Unknown command: ${parsed.command}`);
|
|
4284
|
+
console.error("");
|
|
4285
|
+
console.error('Run "alfred --help" for usage information.');
|
|
4286
|
+
process.exit(1);
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
main().catch((error) => {
|
|
4290
|
+
console.error("Fatal error:", error);
|
|
4291
|
+
process.exit(1);
|
|
4292
|
+
});
|