lovelyai 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.js +504 -3
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -2,15 +2,204 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import ora from "ora";
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
-
import { join } from "node:path";
|
|
8
5
|
import { homedir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { z } from "zod";
|
|
9
9
|
import { createServer } from "node:http";
|
|
10
10
|
import { WebSocketServer } from "ws";
|
|
11
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
12
|
+
import OpenAI from "openai";
|
|
13
|
+
import { createWhatsAppPlugin } from "@lovely/channel-whatsapp";
|
|
14
|
+
import { createTelegramPlugin } from "@lovely/channel-telegram";
|
|
15
|
+
import { createIMessagePlugin } from "@lovely/channel-imessage";
|
|
11
16
|
import { confirm, input, select } from "@inquirer/prompts";
|
|
12
17
|
import { execFileSync } from "node:child_process";
|
|
13
18
|
|
|
19
|
+
//#region ../privacy/dist/index.js
|
|
20
|
+
const PATTERNS = [
|
|
21
|
+
{
|
|
22
|
+
type: "phone",
|
|
23
|
+
re: /\b(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: "email",
|
|
27
|
+
re: /\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b/
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: "ssn",
|
|
31
|
+
re: /\b\d{3}-\d{2}-\d{4}\b/
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
type: "credit_card",
|
|
35
|
+
re: /\b(?:\d[ -]?){13,16}\b/
|
|
36
|
+
}
|
|
37
|
+
];
|
|
38
|
+
function detectPII(text) {
|
|
39
|
+
const types = PATTERNS.filter((p) => p.re.test(text)).map((p) => p.type);
|
|
40
|
+
return {
|
|
41
|
+
hasPII: types.length > 0,
|
|
42
|
+
types
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const CLOUD_PROVIDERS = new Set([
|
|
46
|
+
"anthropic",
|
|
47
|
+
"openai",
|
|
48
|
+
"gemini",
|
|
49
|
+
"bedrock",
|
|
50
|
+
"azure"
|
|
51
|
+
]);
|
|
52
|
+
function isCloudProvider(provider) {
|
|
53
|
+
return CLOUD_PROVIDERS.has(provider.toLowerCase());
|
|
54
|
+
}
|
|
55
|
+
function createPrivacyGate(config) {
|
|
56
|
+
return {
|
|
57
|
+
detectPII,
|
|
58
|
+
enforce(msg, model) {
|
|
59
|
+
const effectivePolicy = config.perConversation[msg.from] ?? config.default;
|
|
60
|
+
const pii = config.piiDetection ? detectPII(msg.text) : void 0;
|
|
61
|
+
const cloud = isCloudProvider(model.provider);
|
|
62
|
+
const piiField = pii !== void 0 ? { pii } : {};
|
|
63
|
+
if (effectivePolicy === "local-only" && cloud) return {
|
|
64
|
+
allow: false,
|
|
65
|
+
routedTo: "blocked",
|
|
66
|
+
reason: `policy is local-only for ${msg.from}`,
|
|
67
|
+
...piiField
|
|
68
|
+
};
|
|
69
|
+
if (effectivePolicy === "local-first" && cloud) {
|
|
70
|
+
if (!config.cloudAllowed.includes(model.provider)) return {
|
|
71
|
+
allow: false,
|
|
72
|
+
routedTo: "blocked",
|
|
73
|
+
reason: `${model.provider} not in cloudAllowed list`,
|
|
74
|
+
...piiField
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
allow: true,
|
|
79
|
+
routedTo: cloud ? "cloud" : "local",
|
|
80
|
+
...piiField
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region ../hitl/dist/index.js
|
|
88
|
+
function createHITLEngine(config) {
|
|
89
|
+
const approvals = /* @__PURE__ */ new Map();
|
|
90
|
+
function approvalKey(actionType, from) {
|
|
91
|
+
return `${from}:${actionType}`;
|
|
92
|
+
}
|
|
93
|
+
function getApprovalRecord(actionType, from) {
|
|
94
|
+
return approvals.get(approvalKey(actionType, from));
|
|
95
|
+
}
|
|
96
|
+
function isExpired(record) {
|
|
97
|
+
const decayMs = config.approvalDecayDays * 24 * 60 * 60 * 1e3;
|
|
98
|
+
return Date.now() - record.lastApprovedAt > decayMs;
|
|
99
|
+
}
|
|
100
|
+
function resolveRisk(actionType) {
|
|
101
|
+
return config.riskThresholds[actionType] ?? "approval";
|
|
102
|
+
}
|
|
103
|
+
function recordApproval(actionType, from) {
|
|
104
|
+
const key = approvalKey(actionType, from);
|
|
105
|
+
const existing = approvals.get(key);
|
|
106
|
+
approvals.set(key, {
|
|
107
|
+
actionType,
|
|
108
|
+
from,
|
|
109
|
+
count: (existing?.count ?? 0) + 1,
|
|
110
|
+
lastApprovedAt: Date.now()
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function revokeApproval(actionType, from) {
|
|
114
|
+
approvals.delete(approvalKey(actionType, from));
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
async evaluate(ctx) {
|
|
118
|
+
if (!config.enabled) return { kind: "proceed" };
|
|
119
|
+
const risk = resolveRisk(ctx.type);
|
|
120
|
+
if (risk === "none") return { kind: "proceed" };
|
|
121
|
+
const record = getApprovalRecord(ctx.type, ctx.from);
|
|
122
|
+
if (record && !isExpired(record) && record.count >= config.approvalMemoryCount) return {
|
|
123
|
+
kind: "fyi",
|
|
124
|
+
message: `FYI: About to ${ctx.description}`
|
|
125
|
+
};
|
|
126
|
+
if (risk === "fyi") return {
|
|
127
|
+
kind: "fyi",
|
|
128
|
+
message: `FYI: ${ctx.description}`
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
kind: "ask",
|
|
132
|
+
question: `${ctx.description} — approve? (yes/no)`,
|
|
133
|
+
onReply: (reply) => {
|
|
134
|
+
if (reply.trim().toLowerCase().startsWith("y")) recordApproval(ctx.type, ctx.from);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
recordApproval,
|
|
139
|
+
revokeApproval
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region ../agent/dist/index.js
|
|
145
|
+
function sessionFilePath(storeDir, sessionKey) {
|
|
146
|
+
return join(storeDir, `${sessionKey.replace(/[^a-zA-Z0-9_\-:]/g, "_")}.jsonl`);
|
|
147
|
+
}
|
|
148
|
+
function createSessionManager(opts) {
|
|
149
|
+
mkdirSync(opts.storeDir, { recursive: true });
|
|
150
|
+
return {
|
|
151
|
+
async append(sessionKey, message) {
|
|
152
|
+
appendFileSync(sessionFilePath(opts.storeDir, sessionKey), JSON.stringify(message) + "\n", "utf8");
|
|
153
|
+
},
|
|
154
|
+
async load(sessionKey) {
|
|
155
|
+
const path = sessionFilePath(opts.storeDir, sessionKey);
|
|
156
|
+
if (!existsSync(path)) return [];
|
|
157
|
+
return readFileSync(path, "utf8").split("\n").filter(Boolean).flatMap((l, i) => {
|
|
158
|
+
try {
|
|
159
|
+
return [JSON.parse(l)];
|
|
160
|
+
} catch {
|
|
161
|
+
console.warn(`[session] Skipping malformed line ${i + 1}`);
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
async clear(sessionKey) {
|
|
167
|
+
const path = sessionFilePath(opts.storeDir, sessionKey);
|
|
168
|
+
if (existsSync(path)) unlinkSync(path);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function createAgentRunner(opts) {
|
|
173
|
+
const sessions = createSessionManager({ storeDir: opts.sessionStoreDir });
|
|
174
|
+
return { async run({ sessionKey, userMessage, onDelta, onEnd }) {
|
|
175
|
+
const history = await sessions.load(sessionKey);
|
|
176
|
+
history.push({
|
|
177
|
+
role: "user",
|
|
178
|
+
content: userMessage
|
|
179
|
+
});
|
|
180
|
+
const req = {
|
|
181
|
+
messages: history,
|
|
182
|
+
...opts.systemPrompt !== void 0 && { systemPrompt: opts.systemPrompt },
|
|
183
|
+
...opts.maxTokens !== void 0 && { maxTokens: opts.maxTokens }
|
|
184
|
+
};
|
|
185
|
+
let assistantText = "";
|
|
186
|
+
for await (const chunk of opts.llm.stream(req)) if (chunk.type === "text") {
|
|
187
|
+
assistantText += chunk.delta;
|
|
188
|
+
onDelta(chunk.delta);
|
|
189
|
+
} else if (chunk.type === "end") onEnd?.(chunk.usage);
|
|
190
|
+
else if (chunk.type === "error") throw new Error(chunk.message);
|
|
191
|
+
await sessions.append(sessionKey, {
|
|
192
|
+
role: "user",
|
|
193
|
+
content: userMessage
|
|
194
|
+
});
|
|
195
|
+
await sessions.append(sessionKey, {
|
|
196
|
+
role: "assistant",
|
|
197
|
+
content: assistantText
|
|
198
|
+
});
|
|
199
|
+
} };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
//#endregion
|
|
14
203
|
//#region ../core/dist/index.js
|
|
15
204
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
16
205
|
const GatewayConfigSchema = z.object({
|
|
@@ -197,9 +386,303 @@ async function createGatewayServer(opts) {
|
|
|
197
386
|
})
|
|
198
387
|
};
|
|
199
388
|
}
|
|
389
|
+
function createDispatcher(opts) {
|
|
390
|
+
const gate = createPrivacyGate(opts.privacy);
|
|
391
|
+
createHITLEngine(opts.hitl);
|
|
392
|
+
const runner = createAgentRunner({
|
|
393
|
+
llm: opts.llm,
|
|
394
|
+
sessionStoreDir: opts.sessionStoreDir,
|
|
395
|
+
systemPrompt: opts.systemPrompt ?? "You are Lovely, a helpful personal AI assistant."
|
|
396
|
+
});
|
|
397
|
+
return { async dispatch(ctx) {
|
|
398
|
+
const providerName = opts.llm.provider ?? opts.llm.id.split(":")[0] ?? "local";
|
|
399
|
+
const decision = gate.enforce(ctx, {
|
|
400
|
+
provider: providerName,
|
|
401
|
+
model: opts.llm.id
|
|
402
|
+
});
|
|
403
|
+
if (!decision.allow) {
|
|
404
|
+
await opts.onReply({
|
|
405
|
+
to: ctx.from,
|
|
406
|
+
channel: ctx.channel,
|
|
407
|
+
text: `Message blocked by privacy policy: ${decision.reason ?? "unknown reason"}`
|
|
408
|
+
});
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
const sessionKey = `${ctx.channel}:${ctx.from}`;
|
|
412
|
+
let fullResponse = "";
|
|
413
|
+
try {
|
|
414
|
+
await runner.run({
|
|
415
|
+
sessionKey,
|
|
416
|
+
userMessage: ctx.text,
|
|
417
|
+
onDelta: (delta) => {
|
|
418
|
+
fullResponse += delta;
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
} catch (err) {
|
|
422
|
+
await opts.onReply({
|
|
423
|
+
to: ctx.from,
|
|
424
|
+
channel: ctx.channel,
|
|
425
|
+
text: "Sorry, something went wrong. Please try again."
|
|
426
|
+
});
|
|
427
|
+
throw err;
|
|
428
|
+
}
|
|
429
|
+
await opts.onReply({
|
|
430
|
+
to: ctx.from,
|
|
431
|
+
channel: ctx.channel,
|
|
432
|
+
text: fullResponse
|
|
433
|
+
});
|
|
434
|
+
} };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region ../llm/dist/index.js
|
|
439
|
+
function createLLMRouter(opts) {
|
|
440
|
+
const { primary, fallbacks } = opts;
|
|
441
|
+
return {
|
|
442
|
+
id: "router",
|
|
443
|
+
async *stream(req) {
|
|
444
|
+
const adapters = [primary, ...fallbacks];
|
|
445
|
+
for (const adapter of adapters) {
|
|
446
|
+
let committed = false;
|
|
447
|
+
for await (const chunk of adapter.stream(req)) {
|
|
448
|
+
if (chunk.type === "error" && !committed) break;
|
|
449
|
+
committed = true;
|
|
450
|
+
yield chunk;
|
|
451
|
+
}
|
|
452
|
+
if (committed) return;
|
|
453
|
+
}
|
|
454
|
+
yield {
|
|
455
|
+
type: "error",
|
|
456
|
+
message: "all providers failed"
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
function createAnthropicAdapter(opts) {
|
|
462
|
+
const client = new Anthropic({ apiKey: opts.apiKey });
|
|
463
|
+
return {
|
|
464
|
+
id: `anthropic:${opts.model}`,
|
|
465
|
+
async *stream(req) {
|
|
466
|
+
try {
|
|
467
|
+
const stream = client.messages.stream({
|
|
468
|
+
model: opts.model,
|
|
469
|
+
max_tokens: req.maxTokens ?? 8192,
|
|
470
|
+
...req.systemPrompt !== void 0 ? { system: req.systemPrompt } : {},
|
|
471
|
+
messages: req.messages.filter((m) => m.role !== "system").map((m) => ({
|
|
472
|
+
role: m.role,
|
|
473
|
+
content: m.content
|
|
474
|
+
}))
|
|
475
|
+
});
|
|
476
|
+
for await (const event of stream) if (event.type === "content_block_delta" && event.delta.type === "text_delta") yield {
|
|
477
|
+
type: "text",
|
|
478
|
+
delta: event.delta.text
|
|
479
|
+
};
|
|
480
|
+
const final = await stream.finalMessage();
|
|
481
|
+
yield {
|
|
482
|
+
type: "end",
|
|
483
|
+
usage: {
|
|
484
|
+
inputTokens: final.usage.input_tokens,
|
|
485
|
+
outputTokens: final.usage.output_tokens
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
} catch (e) {
|
|
489
|
+
yield {
|
|
490
|
+
type: "error",
|
|
491
|
+
message: String(e)
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function createOllamaAdapter(opts) {
|
|
498
|
+
const baseUrl = opts.baseUrl ?? "http://127.0.0.1:11434";
|
|
499
|
+
return {
|
|
500
|
+
id: `ollama:${opts.model}`,
|
|
501
|
+
async *stream(req) {
|
|
502
|
+
try {
|
|
503
|
+
const messages = req.systemPrompt ? [{
|
|
504
|
+
role: "system",
|
|
505
|
+
content: req.systemPrompt
|
|
506
|
+
}, ...req.messages] : req.messages;
|
|
507
|
+
const res = await fetch(`${baseUrl}/api/chat`, {
|
|
508
|
+
method: "POST",
|
|
509
|
+
headers: { "Content-Type": "application/json" },
|
|
510
|
+
body: JSON.stringify({
|
|
511
|
+
model: opts.model,
|
|
512
|
+
messages,
|
|
513
|
+
stream: true
|
|
514
|
+
})
|
|
515
|
+
});
|
|
516
|
+
if (!res.ok || !res.body) {
|
|
517
|
+
yield {
|
|
518
|
+
type: "error",
|
|
519
|
+
message: `Ollama HTTP ${res.status}`
|
|
520
|
+
};
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const reader = res.body.getReader();
|
|
524
|
+
const decoder = new TextDecoder();
|
|
525
|
+
let inputTokens = 0;
|
|
526
|
+
let outputTokens = 0;
|
|
527
|
+
while (true) {
|
|
528
|
+
const { done, value } = await reader.read();
|
|
529
|
+
if (done) break;
|
|
530
|
+
for (const line of decoder.decode(value).split("\n")) {
|
|
531
|
+
if (!line.trim()) continue;
|
|
532
|
+
const parsed = JSON.parse(line);
|
|
533
|
+
const msg = parsed["message"];
|
|
534
|
+
if (msg?.["content"]) yield {
|
|
535
|
+
type: "text",
|
|
536
|
+
delta: String(msg["content"])
|
|
537
|
+
};
|
|
538
|
+
if (parsed["done"]) {
|
|
539
|
+
inputTokens = Number(parsed["prompt_eval_count"] ?? 0);
|
|
540
|
+
outputTokens = Number(parsed["eval_count"] ?? 0);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
yield {
|
|
545
|
+
type: "end",
|
|
546
|
+
usage: {
|
|
547
|
+
inputTokens,
|
|
548
|
+
outputTokens
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
} catch (e) {
|
|
552
|
+
yield {
|
|
553
|
+
type: "error",
|
|
554
|
+
message: String(e)
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
function createOpenAIAdapter(opts) {
|
|
561
|
+
const client = new OpenAI({ apiKey: opts.apiKey });
|
|
562
|
+
return {
|
|
563
|
+
id: `openai:${opts.model}`,
|
|
564
|
+
provider: "openai",
|
|
565
|
+
async *stream(req) {
|
|
566
|
+
try {
|
|
567
|
+
const messages = [];
|
|
568
|
+
if (req.systemPrompt) messages.push({
|
|
569
|
+
role: "system",
|
|
570
|
+
content: req.systemPrompt
|
|
571
|
+
});
|
|
572
|
+
for (const m of req.messages) {
|
|
573
|
+
if (m.role === "system") continue;
|
|
574
|
+
messages.push({
|
|
575
|
+
role: m.role,
|
|
576
|
+
content: m.content
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
const stream = await client.chat.completions.create({
|
|
580
|
+
model: opts.model,
|
|
581
|
+
messages,
|
|
582
|
+
max_tokens: req.maxTokens ?? 8192,
|
|
583
|
+
stream: true
|
|
584
|
+
});
|
|
585
|
+
let inputTokens = 0;
|
|
586
|
+
let outputTokens = 0;
|
|
587
|
+
for await (const chunk of stream) {
|
|
588
|
+
const delta = chunk.choices[0]?.delta?.content;
|
|
589
|
+
if (delta) yield {
|
|
590
|
+
type: "text",
|
|
591
|
+
delta
|
|
592
|
+
};
|
|
593
|
+
if (chunk.usage) {
|
|
594
|
+
inputTokens = chunk.usage.prompt_tokens;
|
|
595
|
+
outputTokens = chunk.usage.completion_tokens;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
yield {
|
|
599
|
+
type: "end",
|
|
600
|
+
usage: {
|
|
601
|
+
inputTokens,
|
|
602
|
+
outputTokens
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
} catch (e) {
|
|
606
|
+
yield {
|
|
607
|
+
type: "error",
|
|
608
|
+
message: String(e)
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
}
|
|
200
614
|
|
|
201
615
|
//#endregion
|
|
202
616
|
//#region src/commands/gateway.ts
|
|
617
|
+
function buildLLMAdapter(config) {
|
|
618
|
+
const { primary, fallbacks, providers } = config.llm;
|
|
619
|
+
function makeAdapter(provider, model) {
|
|
620
|
+
const provCfg = providers[provider] ?? {};
|
|
621
|
+
if (provider === "anthropic") {
|
|
622
|
+
if (!provCfg.apiKey) throw new Error("Anthropic apiKey missing in config");
|
|
623
|
+
return createAnthropicAdapter({
|
|
624
|
+
apiKey: provCfg.apiKey,
|
|
625
|
+
model
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
if (provider === "openai") {
|
|
629
|
+
if (!provCfg.apiKey) throw new Error("OpenAI apiKey missing in config");
|
|
630
|
+
return createOpenAIAdapter({
|
|
631
|
+
apiKey: provCfg.apiKey,
|
|
632
|
+
model
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
if (provider === "ollama") return createOllamaAdapter({
|
|
636
|
+
baseUrl: provCfg.baseUrl ?? "http://localhost:11434",
|
|
637
|
+
model
|
|
638
|
+
});
|
|
639
|
+
throw new Error(`Unknown LLM provider: ${provider}`);
|
|
640
|
+
}
|
|
641
|
+
return createLLMRouter({
|
|
642
|
+
primary: makeAdapter(primary.provider, primary.model),
|
|
643
|
+
fallbacks: fallbacks.map((f) => makeAdapter(f.provider, f.model))
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
async function startChannels(config, onMessage) {
|
|
647
|
+
const channels = config.channels;
|
|
648
|
+
const started = [];
|
|
649
|
+
const home = homedir();
|
|
650
|
+
if (channels["whatsapp"]?.enabled) {
|
|
651
|
+
const authDir = (channels["whatsapp"].authDir ?? "~/.lovely/whatsapp-auth").replace("~", home);
|
|
652
|
+
mkdirSync(authDir, { recursive: true });
|
|
653
|
+
const plugin = createWhatsAppPlugin({
|
|
654
|
+
authDir,
|
|
655
|
+
accountId: channels["whatsapp"].accountId ?? "default"
|
|
656
|
+
});
|
|
657
|
+
await plugin.start({
|
|
658
|
+
onMessage,
|
|
659
|
+
config: channels["whatsapp"]
|
|
660
|
+
});
|
|
661
|
+
started.push(plugin);
|
|
662
|
+
console.log(chalk.dim(" WhatsApp channel started"));
|
|
663
|
+
}
|
|
664
|
+
if (channels["telegram"]?.enabled) {
|
|
665
|
+
const botToken = channels["telegram"].botToken;
|
|
666
|
+
if (!botToken) throw new Error("Telegram botToken missing in config");
|
|
667
|
+
const plugin = createTelegramPlugin({ botToken });
|
|
668
|
+
await plugin.start({
|
|
669
|
+
onMessage,
|
|
670
|
+
config: channels["telegram"]
|
|
671
|
+
});
|
|
672
|
+
started.push(plugin);
|
|
673
|
+
console.log(chalk.dim(" Telegram channel started"));
|
|
674
|
+
}
|
|
675
|
+
if (channels["imessage"]?.enabled) {
|
|
676
|
+
const plugin = createIMessagePlugin({});
|
|
677
|
+
await plugin.start({
|
|
678
|
+
onMessage,
|
|
679
|
+
config: channels["imessage"]
|
|
680
|
+
});
|
|
681
|
+
started.push(plugin);
|
|
682
|
+
console.log(chalk.dim(" iMessage channel started"));
|
|
683
|
+
}
|
|
684
|
+
return started;
|
|
685
|
+
}
|
|
203
686
|
function gatewayCommand() {
|
|
204
687
|
return new Command("gateway").description("Start the Lovely gateway daemon").option("-p, --port <port>", "Port to listen on", "18900").option("--verbose", "Enable verbose logging").action(async (opts) => {
|
|
205
688
|
const config = loadConfig();
|
|
@@ -209,15 +692,33 @@ function gatewayCommand() {
|
|
|
209
692
|
process.exit(1);
|
|
210
693
|
}
|
|
211
694
|
const spinner = ora(`Starting Lovely gateway on port ${port}...`).start();
|
|
695
|
+
let channels = [];
|
|
212
696
|
try {
|
|
697
|
+
const llm = buildLLMAdapter(config);
|
|
698
|
+
const sessionStoreDir = join(homedir(), ".lovely", "sessions");
|
|
699
|
+
mkdirSync(sessionStoreDir, { recursive: true });
|
|
700
|
+
const dispatcher = createDispatcher({
|
|
701
|
+
privacy: config.privacy,
|
|
702
|
+
hitl: config.hitl,
|
|
703
|
+
llm,
|
|
704
|
+
sessionStoreDir,
|
|
705
|
+
onReply: async ({ to, channel: channelId, text }) => {
|
|
706
|
+
const ch = channels.find((c) => c.id === channelId);
|
|
707
|
+
if (ch) await ch.send(to, { text });
|
|
708
|
+
else if (opts.verbose) console.log(chalk.dim(`[reply] no channel for id=${channelId}`));
|
|
709
|
+
}
|
|
710
|
+
});
|
|
213
711
|
const server = await createGatewayServer({
|
|
214
712
|
port,
|
|
215
713
|
host: config.gateway.host
|
|
216
714
|
});
|
|
217
715
|
spinner.succeed(chalk.green(`Lovely gateway running on ${chalk.bold(`http://127.0.0.1:${port}`)}`));
|
|
716
|
+
channels = await startChannels(config, (ctx) => dispatcher.dispatch(ctx));
|
|
717
|
+
if (Object.entries(config.channels).filter(([, c]) => c.enabled).map(([id]) => id).length === 0) console.log(chalk.yellow(" No channels enabled. Edit ~/.lovely/lovely.json to add channels."));
|
|
218
718
|
console.log(chalk.dim(" Press Ctrl+C to stop"));
|
|
219
719
|
const shutdown = async () => {
|
|
220
720
|
console.log("\n" + chalk.yellow("Shutting down..."));
|
|
721
|
+
await Promise.allSettled(channels.map((c) => c.stop()));
|
|
221
722
|
await server.stop();
|
|
222
723
|
process.exit(0);
|
|
223
724
|
};
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../core/dist/index.js","../../src/commands/gateway.ts","../../src/commands/onboard.ts","../../src/index.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { z } from \"zod\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createServer } from \"node:http\";\nimport { WebSocketServer } from \"ws\";\nimport { createPrivacyGate } from \"@lovely/privacy\";\nimport { createHITLEngine } from \"@lovely/hitl\";\nimport { createAgentRunner } from \"@lovely/agent\";\n\n//#region \\0rolldown/runtime.js\nvar __require = /* @__PURE__ */ createRequire(import.meta.url);\n\n//#endregion\n//#region src/types.ts\nconst defaultRuntime = {\n\tlog: (msg) => console.log(`[lovely] ${msg}`),\n\terror: (msg) => console.error(`[lovely:error] ${msg}`),\n\tdebug: (msg) => {\n\t\tif (process.env.LOVELY_DEBUG) console.debug(`[lovely:debug] ${msg}`);\n\t}\n};\n\n//#endregion\n//#region src/config/types.ts\nconst GatewayConfigSchema = z.object({\n\tport: z.number().int().min(1024).max(65535).default(18900),\n\thost: z.string().default(\"127.0.0.1\"),\n\tauth: z.enum([\n\t\t\"none\",\n\t\t\"token\",\n\t\t\"password\"\n\t]).default(\"none\"),\n\ttoken: z.string().optional()\n});\nconst PrivacyConfigSchema = z.object({\n\tdefault: z.enum([\n\t\t\"local-first\",\n\t\t\"cloud-allowed\",\n\t\t\"local-only\"\n\t]).default(\"local-first\"),\n\tcloudAllowed: z.array(z.string()).default([]),\n\tauditLog: z.boolean().default(true),\n\tpiiDetection: z.boolean().default(true),\n\tperConversation: z.record(z.string(), z.enum([\n\t\t\"local-only\",\n\t\t\"cloud-allowed\",\n\t\t\"local-first\"\n\t])).default({})\n});\nconst HitlConfigSchema = z.object({\n\tenabled: z.boolean().default(true),\n\tdisambiguationThreshold: z.number().min(0).max(1).default(.7),\n\tapprovalMemoryCount: z.number().int().min(1).default(2),\n\tapprovalDecayDays: z.number().int().min(1).default(30),\n\triskThresholds: z.record(z.string(), z.enum([\n\t\t\"none\",\n\t\t\"fyi\",\n\t\t\"approval\"\n\t])).default({\n\t\tsendMessage: \"fyi\",\n\t\tsendEmail: \"approval\",\n\t\tdeleteFile: \"approval\",\n\t\texecuteCode: \"approval\",\n\t\twebSearch: \"fyi\",\n\t\treadFile: \"none\"\n\t})\n});\nconst LLMPrimarySchema = z.object({\n\tprovider: z.string().default(\"anthropic\"),\n\tmodel: z.string().default(\"claude-sonnet-4-6\")\n}).default({});\nconst LLMConfigSchema = z.object({\n\tprimary: LLMPrimarySchema,\n\tfallbacks: z.array(z.object({\n\t\tprovider: z.string(),\n\t\tmodel: z.string()\n\t})).default([]),\n\tproviders: z.record(z.string(), z.object({\n\t\tbaseUrl: z.string().optional(),\n\t\tapiKey: z.string().optional(),\n\t\ttype: z.enum([\n\t\t\t\"anthropic\",\n\t\t\t\"openai\",\n\t\t\t\"ollama\",\n\t\t\t\"custom\"\n\t\t]).default(\"custom\")\n\t})).default({})\n});\nconst LovelyConfigSchema = z.object({\n\tgateway: GatewayConfigSchema.default({}),\n\tprivacy: PrivacyConfigSchema.default({}),\n\thitl: HitlConfigSchema.default({}),\n\tllm: LLMConfigSchema.default({})\n});\n\n//#endregion\n//#region src/config/load.ts\nfunction resolveConfigDir(opts = {}) {\n\treturn opts.configDir ?? join(homedir(), \".lovely\");\n}\nfunction loadConfig(opts = {}) {\n\tconst configDir = resolveConfigDir(opts);\n\tconst configFile = opts.configFile ?? join(configDir, \"lovely.json\");\n\tlet raw = {};\n\tif (existsSync(configFile)) {\n\t\tconst content = readFileSync(configFile, \"utf8\");\n\t\ttry {\n\t\t\traw = JSON.parse(content);\n\t\t} catch (err) {\n\t\t\tthrow new Error(`Failed to parse config file ${configFile}: ${err.message}`);\n\t\t}\n\t}\n\treturn LovelyConfigSchema.parse(raw);\n}\n\n//#endregion\n//#region src/events/bus.ts\nfunction createEventBus() {\n\tconst handlers = /* @__PURE__ */ new Map();\n\tconst anyHandlers = /* @__PURE__ */ new Set();\n\treturn {\n\t\ton(event, handler) {\n\t\t\tif (!handlers.has(event)) handlers.set(event, /* @__PURE__ */ new Set());\n\t\t\thandlers.get(event).add(handler);\n\t\t\treturn () => {\n\t\t\t\thandlers.get(event)?.delete(handler);\n\t\t\t};\n\t\t},\n\t\tonAny(handler) {\n\t\t\tanyHandlers.add(handler);\n\t\t\treturn () => {\n\t\t\t\tanyHandlers.delete(handler);\n\t\t\t};\n\t\t},\n\t\temit(event, payload) {\n\t\t\thandlers.get(event)?.forEach((h) => h(payload));\n\t\t\tanyHandlers.forEach((h) => h({\n\t\t\t\tevent,\n\t\t\t\tpayload\n\t\t\t}));\n\t\t}\n\t};\n}\n\n//#endregion\n//#region src/protocol/frames.ts\nfunction serializeFrame(frame) {\n\treturn JSON.stringify(frame);\n}\nfunction parseFrame(raw) {\n\ttry {\n\t\tconst parsed = JSON.parse(raw);\n\t\tif (typeof parsed !== \"object\" || parsed === null) return null;\n\t\tconst obj = parsed;\n\t\tif (obj[\"type\"] !== \"req\" && obj[\"type\"] !== \"res\" && obj[\"type\"] !== \"event\") return null;\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n//#endregion\n//#region src/gateway/server.ts\nconst express = __require(\"express\");\nasync function createGatewayServer(opts) {\n\tconst { port, host = \"127.0.0.1\" } = opts;\n\tconst bus = opts.bus ?? createEventBus();\n\tconst app = express();\n\tapp.use(express.json());\n\tapp.get(\"/health\", (_req, res) => {\n\t\tres.json({\n\t\t\tok: true,\n\t\t\tversion: \"0.0.1\"\n\t\t});\n\t});\n\tconst httpServer = createServer(app);\n\tconst wss = new WebSocketServer({ server: httpServer });\n\tlet seq = 0;\n\twss.on(\"connection\", (ws) => {\n\t\tws.on(\"message\", (data) => {\n\t\t\tconst frame = parseFrame(data.toString());\n\t\t\tif (!frame || frame.type !== \"req\") return;\n\t\t\tws.send(serializeFrame({\n\t\t\t\ttype: \"res\",\n\t\t\t\tid: frame.id,\n\t\t\t\tok: true\n\t\t\t}));\n\t\t});\n\t});\n\tfunction broadcast(event, payload) {\n\t\tconst frame = serializeFrame({\n\t\t\ttype: \"event\",\n\t\t\tevent,\n\t\t\tpayload,\n\t\t\tseq: seq++\n\t\t});\n\t\tfor (const client of wss.clients) if (client.readyState === client.OPEN) client.send(frame);\n\t}\n\tawait new Promise((resolve, reject) => {\n\t\thttpServer.once(\"error\", reject);\n\t\thttpServer.listen(port, host, () => {\n\t\t\thttpServer.off(\"error\", reject);\n\t\t\tresolve();\n\t\t});\n\t});\n\treturn {\n\t\tport,\n\t\tbus,\n\t\tbroadcast,\n\t\tstop: () => new Promise((resolve, reject) => {\n\t\t\twss.close((wsErr) => {\n\t\t\t\tif (wsErr) {\n\t\t\t\t\treject(wsErr);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\thttpServer.close((err) => err ? reject(err) : resolve());\n\t\t\t});\n\t\t})\n\t};\n}\n\n//#endregion\n//#region src/dispatch.ts\nfunction createDispatcher(opts) {\n\tconst gate = createPrivacyGate(opts.privacy);\n\tcreateHITLEngine(opts.hitl);\n\tconst runner = createAgentRunner({\n\t\tllm: opts.llm,\n\t\tsessionStoreDir: opts.sessionStoreDir,\n\t\tsystemPrompt: opts.systemPrompt ?? \"You are Lovely, a helpful personal AI assistant.\"\n\t});\n\treturn { async dispatch(ctx) {\n\t\tconst providerName = opts.llm.provider ?? opts.llm.id.split(\":\")[0] ?? \"local\";\n\t\tconst decision = gate.enforce(ctx, {\n\t\t\tprovider: providerName,\n\t\t\tmodel: opts.llm.id\n\t\t});\n\t\tif (!decision.allow) {\n\t\t\tawait opts.onReply({\n\t\t\t\tto: ctx.from,\n\t\t\t\tchannel: ctx.channel,\n\t\t\t\ttext: `Message blocked by privacy policy: ${decision.reason ?? \"unknown reason\"}`\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst sessionKey = `${ctx.channel}:${ctx.from}`;\n\t\tlet fullResponse = \"\";\n\t\ttry {\n\t\t\tawait runner.run({\n\t\t\t\tsessionKey,\n\t\t\t\tuserMessage: ctx.text,\n\t\t\t\tonDelta: (delta) => {\n\t\t\t\t\tfullResponse += delta;\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tawait opts.onReply({\n\t\t\t\tto: ctx.from,\n\t\t\t\tchannel: ctx.channel,\n\t\t\t\ttext: \"Sorry, something went wrong. Please try again.\"\n\t\t\t});\n\t\t\tthrow err;\n\t\t}\n\t\tawait opts.onReply({\n\t\t\tto: ctx.from,\n\t\t\tchannel: ctx.channel,\n\t\t\ttext: fullResponse\n\t\t});\n\t} };\n}\n\n//#endregion\n//#region src/plugins/loader.ts\nfunction createPluginRegistry() {\n\tconst plugins = [];\n\treturn {\n\t\tregister: (plugin) => plugins.push(plugin),\n\t\tgetAll: () => [...plugins],\n\t\tgetChannels: () => plugins.flatMap((p) => p.channels ?? [])\n\t};\n}\n\n//#endregion\nexport { GatewayConfigSchema, HitlConfigSchema, LLMConfigSchema, LovelyConfigSchema, PrivacyConfigSchema, createDispatcher, createEventBus, createGatewayServer, createPluginRegistry, defaultRuntime, loadConfig, parseFrame, resolveConfigDir, serializeFrame };\n//# sourceMappingURL=index.js.map","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { createGatewayServer, loadConfig } from '@lovely/core';\n\nexport function gatewayCommand(): Command {\n return new Command('gateway')\n .description('Start the Lovely gateway daemon')\n .option('-p, --port <port>', 'Port to listen on', '18900')\n .option('--verbose', 'Enable verbose logging')\n .action(async (opts: { port: string; verbose?: boolean }) => {\n const config = loadConfig();\n const port = parseInt(opts.port, 10);\n\n if (Number.isNaN(port) || port < 1 || port > 65535) {\n console.error(chalk.red(`Invalid port: ${String(opts.port)}`));\n process.exit(1);\n }\n\n const spinner = ora(`Starting Lovely gateway on port ${port}...`).start();\n\n try {\n const server = await createGatewayServer({ port, host: config.gateway.host });\n spinner.succeed(\n chalk.green(`Lovely gateway running on ${chalk.bold(`http://127.0.0.1:${port}`)}`),\n );\n console.log(chalk.dim(' Press Ctrl+C to stop'));\n\n const shutdown = async (): Promise<void> => {\n console.log('\\n' + chalk.yellow('Shutting down...'));\n await server.stop();\n process.exit(0);\n };\n\n process.on('SIGINT', () => { void shutdown(); });\n process.on('SIGTERM', () => { void shutdown(); });\n } catch (err) {\n spinner.fail(chalk.red(`Failed to start gateway: ${err}`));\n process.exit(1);\n }\n });\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { confirm, input, select } from '@inquirer/prompts';\nimport { writeFileSync, mkdirSync, chmodSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { execFileSync } from 'node:child_process';\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nexport function onboardCommand(): Command {\n return new Command('onboard')\n .description('Interactive setup wizard for Lovely')\n .option('--install-daemon', 'Install launchd daemon after setup')\n .action(async (opts: { installDaemon?: boolean }) => {\n console.log(chalk.bold.magenta('\\nWelcome to Lovely setup\\n'));\n\n const configDir = join(homedir(), '.lovely');\n mkdirSync(configDir, { recursive: true });\n\n // Step 1: LLM provider\n const provider = await select({\n message: 'Which AI provider do you want to use?',\n choices: [\n { name: 'Anthropic (Claude) — recommended', value: 'anthropic' },\n { name: 'Ollama (local, privacy-first)', value: 'ollama' },\n { name: 'OpenAI', value: 'openai' },\n { name: 'Skip for now', value: 'skip' },\n ],\n });\n\n let apiKey = '';\n let model = '';\n\n if (provider === 'anthropic') {\n apiKey = await input({\n message: 'Anthropic API key:',\n validate: (v) => v.startsWith('sk-') || 'Must start with sk-',\n });\n model = 'claude-sonnet-4-6';\n } else if (provider === 'openai') {\n apiKey = await input({\n message: 'OpenAI API key:',\n validate: (v) => v.startsWith('sk-') || 'Must start with sk-',\n });\n model = 'gpt-4o';\n } else if (provider === 'ollama') {\n model = await input({ message: 'Ollama model name:', default: 'llama3.2' });\n }\n\n // Step 2: Privacy policy\n const privacy = await select({\n message: 'Default privacy policy:',\n choices: [\n { name: 'Local-first (cloud only if explicitly allowed)', value: 'local-first' },\n { name: 'Local-only (never send to cloud)', value: 'local-only' },\n { name: 'Cloud-allowed (use cloud freely)', value: 'cloud-allowed' },\n ],\n });\n\n // Step 3: iMessage\n const enableIMessage = await confirm({ message: 'Enable iMessage channel?', default: true });\n\n // Write config\n const config = {\n gateway: { port: 18900 },\n privacy: {\n default: privacy,\n cloudAllowed: privacy !== 'local-only' && provider !== 'skip' ? [provider] : [],\n },\n hitl: { enabled: true },\n llm: {\n primary: { provider, model },\n providers:\n provider !== 'ollama' && provider !== 'skip'\n ? { [provider]: { apiKey, type: provider } }\n : {},\n },\n channels: {\n imessage: { enabled: enableIMessage },\n },\n };\n\n const configFilePath = join(configDir, 'lovely.json');\n writeFileSync(configFilePath, JSON.stringify(config, null, 2), { mode: 0o600 });\n chmodSync(configFilePath, 0o600);\n console.log(chalk.green(`\\n✓ Config written to ${configDir}/lovely.json (permissions: 600)`));\n\n if (opts.installDaemon === true) {\n installLaunchdDaemon();\n }\n\n console.log(chalk.bold('\\nSetup complete! Run: ') + chalk.cyan('lovely gateway') + '\\n');\n });\n}\n\nfunction installLaunchdDaemon(): void {\n if (process.platform !== 'darwin') {\n console.error(chalk.red('--install-daemon is only supported on macOS'));\n process.exit(1);\n }\n\n const binPath = process.argv[1] ?? 'lovely';\n\n const plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>ai.lovely.gateway</string>\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(process.execPath)}</string>\n <string>${escapeXml(binPath)}</string>\n <string>gateway</string>\n </array>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <true/>\n <key>StandardOutPath</key>\n <string>${homedir()}/.lovely/logs/gateway.log</string>\n <key>StandardErrorPath</key>\n <string>${homedir()}/.lovely/logs/gateway.error.log</string>\n</dict>\n</plist>`;\n\n const launchAgentsDir = join(homedir(), 'Library', 'LaunchAgents');\n mkdirSync(launchAgentsDir, { recursive: true });\n mkdirSync(join(homedir(), '.lovely', 'logs'), { recursive: true });\n\n const plistPath = join(launchAgentsDir, 'ai.lovely.gateway.plist');\n writeFileSync(plistPath, plist);\n\n execFileSync('launchctl', ['load', plistPath]);\n console.log(chalk.green('Launchd daemon installed — Lovely will start automatically on login'));\n}\n","import { Command } from 'commander';\nimport { gatewayCommand } from './commands/gateway.js';\nimport { onboardCommand } from './commands/onboard.js';\n\nconst program = new Command();\n\nprogram\n .name('lovely')\n .description('Lovely — your personal AGI. Runs on your hardware.')\n .version(process.env['npm_package_version'] ?? '0.0.1');\n\nprogram.addCommand(gatewayCommand());\nprogram.addCommand(onboardCommand());\n\nprogram.parse(process.argv);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../privacy/dist/index.js","../../../hitl/dist/index.js","../../../agent/dist/index.js","../../../core/dist/index.js","../../../llm/dist/index.js","../../src/commands/gateway.ts","../../src/commands/onboard.ts","../../src/index.ts"],"sourcesContent":["//#region src/pii.ts\nconst PATTERNS = [\n\t{\n\t\ttype: \"phone\",\n\t\tre: /\\b(\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b/\n\t},\n\t{\n\t\ttype: \"email\",\n\t\tre: /\\b[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}\\b/\n\t},\n\t{\n\t\ttype: \"ssn\",\n\t\tre: /\\b\\d{3}-\\d{2}-\\d{4}\\b/\n\t},\n\t{\n\t\ttype: \"credit_card\",\n\t\tre: /\\b(?:\\d[ -]?){13,16}\\b/\n\t}\n];\nfunction detectPII(text) {\n\tconst types = PATTERNS.filter((p) => p.re.test(text)).map((p) => p.type);\n\treturn {\n\t\thasPII: types.length > 0,\n\t\ttypes\n\t};\n}\n\n//#endregion\n//#region src/gate.ts\nconst CLOUD_PROVIDERS = new Set([\n\t\"anthropic\",\n\t\"openai\",\n\t\"gemini\",\n\t\"bedrock\",\n\t\"azure\"\n]);\nfunction isCloudProvider(provider) {\n\treturn CLOUD_PROVIDERS.has(provider.toLowerCase());\n}\nfunction createPrivacyGate(config) {\n\treturn {\n\t\tdetectPII,\n\t\tenforce(msg, model) {\n\t\t\tconst effectivePolicy = config.perConversation[msg.from] ?? config.default;\n\t\t\tconst pii = config.piiDetection ? detectPII(msg.text) : void 0;\n\t\t\tconst cloud = isCloudProvider(model.provider);\n\t\t\tconst piiField = pii !== void 0 ? { pii } : {};\n\t\t\tif (effectivePolicy === \"local-only\" && cloud) return {\n\t\t\t\tallow: false,\n\t\t\t\troutedTo: \"blocked\",\n\t\t\t\treason: `policy is local-only for ${msg.from}`,\n\t\t\t\t...piiField\n\t\t\t};\n\t\t\tif (effectivePolicy === \"local-first\" && cloud) {\n\t\t\t\tif (!config.cloudAllowed.includes(model.provider)) return {\n\t\t\t\t\tallow: false,\n\t\t\t\t\troutedTo: \"blocked\",\n\t\t\t\t\treason: `${model.provider} not in cloudAllowed list`,\n\t\t\t\t\t...piiField\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tallow: true,\n\t\t\t\troutedTo: cloud ? \"cloud\" : \"local\",\n\t\t\t\t...piiField\n\t\t\t};\n\t\t}\n\t};\n}\n\n//#endregion\nexport { createPrivacyGate, detectPII };\n//# sourceMappingURL=index.js.map","//#region src/engine.ts\nfunction createHITLEngine(config) {\n\tconst approvals = /* @__PURE__ */ new Map();\n\tfunction approvalKey(actionType, from) {\n\t\treturn `${from}:${actionType}`;\n\t}\n\tfunction getApprovalRecord(actionType, from) {\n\t\treturn approvals.get(approvalKey(actionType, from));\n\t}\n\tfunction isExpired(record) {\n\t\tconst decayMs = config.approvalDecayDays * 24 * 60 * 60 * 1e3;\n\t\treturn Date.now() - record.lastApprovedAt > decayMs;\n\t}\n\tfunction resolveRisk(actionType) {\n\t\treturn config.riskThresholds[actionType] ?? \"approval\";\n\t}\n\tfunction recordApproval(actionType, from) {\n\t\tconst key = approvalKey(actionType, from);\n\t\tconst existing = approvals.get(key);\n\t\tapprovals.set(key, {\n\t\t\tactionType,\n\t\t\tfrom,\n\t\t\tcount: (existing?.count ?? 0) + 1,\n\t\t\tlastApprovedAt: Date.now()\n\t\t});\n\t}\n\tfunction revokeApproval(actionType, from) {\n\t\tapprovals.delete(approvalKey(actionType, from));\n\t}\n\treturn {\n\t\tasync evaluate(ctx) {\n\t\t\tif (!config.enabled) return { kind: \"proceed\" };\n\t\t\tconst risk = resolveRisk(ctx.type);\n\t\t\tif (risk === \"none\") return { kind: \"proceed\" };\n\t\t\tconst record = getApprovalRecord(ctx.type, ctx.from);\n\t\t\tif (record && !isExpired(record) && record.count >= config.approvalMemoryCount) return {\n\t\t\t\tkind: \"fyi\",\n\t\t\t\tmessage: `FYI: About to ${ctx.description}`\n\t\t\t};\n\t\t\tif (risk === \"fyi\") return {\n\t\t\t\tkind: \"fyi\",\n\t\t\t\tmessage: `FYI: ${ctx.description}`\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tkind: \"ask\",\n\t\t\t\tquestion: `${ctx.description} — approve? (yes/no)`,\n\t\t\t\tonReply: (reply) => {\n\t\t\t\t\tif (reply.trim().toLowerCase().startsWith(\"y\")) recordApproval(ctx.type, ctx.from);\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\trecordApproval,\n\t\trevokeApproval\n\t};\n}\n\n//#endregion\nexport { createHITLEngine };\n//# sourceMappingURL=index.js.map","import { appendFileSync, existsSync, mkdirSync, readFileSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n//#region src/session/manager.ts\nfunction sessionFilePath(storeDir, sessionKey) {\n\treturn join(storeDir, `${sessionKey.replace(/[^a-zA-Z0-9_\\-:]/g, \"_\")}.jsonl`);\n}\nfunction createSessionManager(opts) {\n\tmkdirSync(opts.storeDir, { recursive: true });\n\treturn {\n\t\tasync append(sessionKey, message) {\n\t\t\tappendFileSync(sessionFilePath(opts.storeDir, sessionKey), JSON.stringify(message) + \"\\n\", \"utf8\");\n\t\t},\n\t\tasync load(sessionKey) {\n\t\t\tconst path = sessionFilePath(opts.storeDir, sessionKey);\n\t\t\tif (!existsSync(path)) return [];\n\t\t\treturn readFileSync(path, \"utf8\").split(\"\\n\").filter(Boolean).flatMap((l, i) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn [JSON.parse(l)];\n\t\t\t\t} catch {\n\t\t\t\t\tconsole.warn(`[session] Skipping malformed line ${i + 1}`);\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tasync clear(sessionKey) {\n\t\t\tconst path = sessionFilePath(opts.storeDir, sessionKey);\n\t\t\tif (existsSync(path)) unlinkSync(path);\n\t\t}\n\t};\n}\n\n//#endregion\n//#region src/runner.ts\nfunction createAgentRunner(opts) {\n\tconst sessions = createSessionManager({ storeDir: opts.sessionStoreDir });\n\treturn { async run({ sessionKey, userMessage, onDelta, onEnd }) {\n\t\tconst history = await sessions.load(sessionKey);\n\t\thistory.push({\n\t\t\trole: \"user\",\n\t\t\tcontent: userMessage\n\t\t});\n\t\tconst req = {\n\t\t\tmessages: history,\n\t\t\t...opts.systemPrompt !== void 0 && { systemPrompt: opts.systemPrompt },\n\t\t\t...opts.maxTokens !== void 0 && { maxTokens: opts.maxTokens }\n\t\t};\n\t\tlet assistantText = \"\";\n\t\tfor await (const chunk of opts.llm.stream(req)) if (chunk.type === \"text\") {\n\t\t\tassistantText += chunk.delta;\n\t\t\tonDelta(chunk.delta);\n\t\t} else if (chunk.type === \"end\") onEnd?.(chunk.usage);\n\t\telse if (chunk.type === \"error\") throw new Error(chunk.message);\n\t\tawait sessions.append(sessionKey, {\n\t\t\trole: \"user\",\n\t\t\tcontent: userMessage\n\t\t});\n\t\tawait sessions.append(sessionKey, {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: assistantText\n\t\t});\n\t} };\n}\n\n//#endregion\nexport { createAgentRunner, createSessionManager };\n//# sourceMappingURL=index.js.map","import { createRequire } from \"node:module\";\nimport { z } from \"zod\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createServer } from \"node:http\";\nimport { WebSocketServer } from \"ws\";\nimport { createPrivacyGate } from \"@lovely/privacy\";\nimport { createHITLEngine } from \"@lovely/hitl\";\nimport { createAgentRunner } from \"@lovely/agent\";\n\n//#region \\0rolldown/runtime.js\nvar __require = /* @__PURE__ */ createRequire(import.meta.url);\n\n//#endregion\n//#region src/types.ts\nconst defaultRuntime = {\n\tlog: (msg) => console.log(`[lovely] ${msg}`),\n\terror: (msg) => console.error(`[lovely:error] ${msg}`),\n\tdebug: (msg) => {\n\t\tif (process.env.LOVELY_DEBUG) console.debug(`[lovely:debug] ${msg}`);\n\t}\n};\n\n//#endregion\n//#region src/config/types.ts\nconst GatewayConfigSchema = z.object({\n\tport: z.number().int().min(1024).max(65535).default(18900),\n\thost: z.string().default(\"127.0.0.1\"),\n\tauth: z.enum([\n\t\t\"none\",\n\t\t\"token\",\n\t\t\"password\"\n\t]).default(\"none\"),\n\ttoken: z.string().optional()\n});\nconst PrivacyConfigSchema = z.object({\n\tdefault: z.enum([\n\t\t\"local-first\",\n\t\t\"cloud-allowed\",\n\t\t\"local-only\"\n\t]).default(\"local-first\"),\n\tcloudAllowed: z.array(z.string()).default([]),\n\tauditLog: z.boolean().default(true),\n\tpiiDetection: z.boolean().default(true),\n\tperConversation: z.record(z.string(), z.enum([\n\t\t\"local-only\",\n\t\t\"cloud-allowed\",\n\t\t\"local-first\"\n\t])).default({})\n});\nconst HitlConfigSchema = z.object({\n\tenabled: z.boolean().default(true),\n\tdisambiguationThreshold: z.number().min(0).max(1).default(.7),\n\tapprovalMemoryCount: z.number().int().min(1).default(2),\n\tapprovalDecayDays: z.number().int().min(1).default(30),\n\triskThresholds: z.record(z.string(), z.enum([\n\t\t\"none\",\n\t\t\"fyi\",\n\t\t\"approval\"\n\t])).default({\n\t\tsendMessage: \"fyi\",\n\t\tsendEmail: \"approval\",\n\t\tdeleteFile: \"approval\",\n\t\texecuteCode: \"approval\",\n\t\twebSearch: \"fyi\",\n\t\treadFile: \"none\"\n\t})\n});\nconst LLMPrimarySchema = z.object({\n\tprovider: z.string().default(\"anthropic\"),\n\tmodel: z.string().default(\"claude-sonnet-4-6\")\n}).default({});\nconst LLMConfigSchema = z.object({\n\tprimary: LLMPrimarySchema,\n\tfallbacks: z.array(z.object({\n\t\tprovider: z.string(),\n\t\tmodel: z.string()\n\t})).default([]),\n\tproviders: z.record(z.string(), z.object({\n\t\tbaseUrl: z.string().optional(),\n\t\tapiKey: z.string().optional(),\n\t\ttype: z.enum([\n\t\t\t\"anthropic\",\n\t\t\t\"openai\",\n\t\t\t\"ollama\",\n\t\t\t\"custom\"\n\t\t]).default(\"custom\")\n\t})).default({})\n});\nconst LovelyConfigSchema = z.object({\n\tgateway: GatewayConfigSchema.default({}),\n\tprivacy: PrivacyConfigSchema.default({}),\n\thitl: HitlConfigSchema.default({}),\n\tllm: LLMConfigSchema.default({})\n});\n\n//#endregion\n//#region src/config/load.ts\nfunction resolveConfigDir(opts = {}) {\n\treturn opts.configDir ?? join(homedir(), \".lovely\");\n}\nfunction loadConfig(opts = {}) {\n\tconst configDir = resolveConfigDir(opts);\n\tconst configFile = opts.configFile ?? join(configDir, \"lovely.json\");\n\tlet raw = {};\n\tif (existsSync(configFile)) {\n\t\tconst content = readFileSync(configFile, \"utf8\");\n\t\ttry {\n\t\t\traw = JSON.parse(content);\n\t\t} catch (err) {\n\t\t\tthrow new Error(`Failed to parse config file ${configFile}: ${err.message}`);\n\t\t}\n\t}\n\treturn LovelyConfigSchema.parse(raw);\n}\n\n//#endregion\n//#region src/events/bus.ts\nfunction createEventBus() {\n\tconst handlers = /* @__PURE__ */ new Map();\n\tconst anyHandlers = /* @__PURE__ */ new Set();\n\treturn {\n\t\ton(event, handler) {\n\t\t\tif (!handlers.has(event)) handlers.set(event, /* @__PURE__ */ new Set());\n\t\t\thandlers.get(event).add(handler);\n\t\t\treturn () => {\n\t\t\t\thandlers.get(event)?.delete(handler);\n\t\t\t};\n\t\t},\n\t\tonAny(handler) {\n\t\t\tanyHandlers.add(handler);\n\t\t\treturn () => {\n\t\t\t\tanyHandlers.delete(handler);\n\t\t\t};\n\t\t},\n\t\temit(event, payload) {\n\t\t\thandlers.get(event)?.forEach((h) => h(payload));\n\t\t\tanyHandlers.forEach((h) => h({\n\t\t\t\tevent,\n\t\t\t\tpayload\n\t\t\t}));\n\t\t}\n\t};\n}\n\n//#endregion\n//#region src/protocol/frames.ts\nfunction serializeFrame(frame) {\n\treturn JSON.stringify(frame);\n}\nfunction parseFrame(raw) {\n\ttry {\n\t\tconst parsed = JSON.parse(raw);\n\t\tif (typeof parsed !== \"object\" || parsed === null) return null;\n\t\tconst obj = parsed;\n\t\tif (obj[\"type\"] !== \"req\" && obj[\"type\"] !== \"res\" && obj[\"type\"] !== \"event\") return null;\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n//#endregion\n//#region src/gateway/server.ts\nconst express = __require(\"express\");\nasync function createGatewayServer(opts) {\n\tconst { port, host = \"127.0.0.1\" } = opts;\n\tconst bus = opts.bus ?? createEventBus();\n\tconst app = express();\n\tapp.use(express.json());\n\tapp.get(\"/health\", (_req, res) => {\n\t\tres.json({\n\t\t\tok: true,\n\t\t\tversion: \"0.0.1\"\n\t\t});\n\t});\n\tconst httpServer = createServer(app);\n\tconst wss = new WebSocketServer({ server: httpServer });\n\tlet seq = 0;\n\twss.on(\"connection\", (ws) => {\n\t\tws.on(\"message\", (data) => {\n\t\t\tconst frame = parseFrame(data.toString());\n\t\t\tif (!frame || frame.type !== \"req\") return;\n\t\t\tws.send(serializeFrame({\n\t\t\t\ttype: \"res\",\n\t\t\t\tid: frame.id,\n\t\t\t\tok: true\n\t\t\t}));\n\t\t});\n\t});\n\tfunction broadcast(event, payload) {\n\t\tconst frame = serializeFrame({\n\t\t\ttype: \"event\",\n\t\t\tevent,\n\t\t\tpayload,\n\t\t\tseq: seq++\n\t\t});\n\t\tfor (const client of wss.clients) if (client.readyState === client.OPEN) client.send(frame);\n\t}\n\tawait new Promise((resolve, reject) => {\n\t\thttpServer.once(\"error\", reject);\n\t\thttpServer.listen(port, host, () => {\n\t\t\thttpServer.off(\"error\", reject);\n\t\t\tresolve();\n\t\t});\n\t});\n\treturn {\n\t\tport,\n\t\tbus,\n\t\tbroadcast,\n\t\tstop: () => new Promise((resolve, reject) => {\n\t\t\twss.close((wsErr) => {\n\t\t\t\tif (wsErr) {\n\t\t\t\t\treject(wsErr);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\thttpServer.close((err) => err ? reject(err) : resolve());\n\t\t\t});\n\t\t})\n\t};\n}\n\n//#endregion\n//#region src/dispatch.ts\nfunction createDispatcher(opts) {\n\tconst gate = createPrivacyGate(opts.privacy);\n\tcreateHITLEngine(opts.hitl);\n\tconst runner = createAgentRunner({\n\t\tllm: opts.llm,\n\t\tsessionStoreDir: opts.sessionStoreDir,\n\t\tsystemPrompt: opts.systemPrompt ?? \"You are Lovely, a helpful personal AI assistant.\"\n\t});\n\treturn { async dispatch(ctx) {\n\t\tconst providerName = opts.llm.provider ?? opts.llm.id.split(\":\")[0] ?? \"local\";\n\t\tconst decision = gate.enforce(ctx, {\n\t\t\tprovider: providerName,\n\t\t\tmodel: opts.llm.id\n\t\t});\n\t\tif (!decision.allow) {\n\t\t\tawait opts.onReply({\n\t\t\t\tto: ctx.from,\n\t\t\t\tchannel: ctx.channel,\n\t\t\t\ttext: `Message blocked by privacy policy: ${decision.reason ?? \"unknown reason\"}`\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst sessionKey = `${ctx.channel}:${ctx.from}`;\n\t\tlet fullResponse = \"\";\n\t\ttry {\n\t\t\tawait runner.run({\n\t\t\t\tsessionKey,\n\t\t\t\tuserMessage: ctx.text,\n\t\t\t\tonDelta: (delta) => {\n\t\t\t\t\tfullResponse += delta;\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tawait opts.onReply({\n\t\t\t\tto: ctx.from,\n\t\t\t\tchannel: ctx.channel,\n\t\t\t\ttext: \"Sorry, something went wrong. Please try again.\"\n\t\t\t});\n\t\t\tthrow err;\n\t\t}\n\t\tawait opts.onReply({\n\t\t\tto: ctx.from,\n\t\t\tchannel: ctx.channel,\n\t\t\ttext: fullResponse\n\t\t});\n\t} };\n}\n\n//#endregion\n//#region src/plugins/loader.ts\nfunction createPluginRegistry() {\n\tconst plugins = [];\n\treturn {\n\t\tregister: (plugin) => plugins.push(plugin),\n\t\tgetAll: () => [...plugins],\n\t\tgetChannels: () => plugins.flatMap((p) => p.channels ?? [])\n\t};\n}\n\n//#endregion\nexport { GatewayConfigSchema, HitlConfigSchema, LLMConfigSchema, LovelyConfigSchema, PrivacyConfigSchema, createDispatcher, createEventBus, createGatewayServer, createPluginRegistry, defaultRuntime, loadConfig, parseFrame, resolveConfigDir, serializeFrame };\n//# sourceMappingURL=index.js.map","import Anthropic from \"@anthropic-ai/sdk\";\nimport OpenAI from \"openai\";\n\n//#region src/router.ts\nfunction createLLMRouter(opts) {\n\tconst { primary, fallbacks } = opts;\n\treturn {\n\t\tid: \"router\",\n\t\tasync *stream(req) {\n\t\t\tconst adapters = [primary, ...fallbacks];\n\t\t\tfor (const adapter of adapters) {\n\t\t\t\tlet committed = false;\n\t\t\t\tfor await (const chunk of adapter.stream(req)) {\n\t\t\t\t\tif (chunk.type === \"error\" && !committed) break;\n\t\t\t\t\tcommitted = true;\n\t\t\t\t\tyield chunk;\n\t\t\t\t}\n\t\t\t\tif (committed) return;\n\t\t\t}\n\t\t\tyield {\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: \"all providers failed\"\n\t\t\t};\n\t\t}\n\t};\n}\n\n//#endregion\n//#region src/adapters/anthropic.ts\nfunction createAnthropicAdapter(opts) {\n\tconst client = new Anthropic({ apiKey: opts.apiKey });\n\treturn {\n\t\tid: `anthropic:${opts.model}`,\n\t\tasync *stream(req) {\n\t\t\ttry {\n\t\t\t\tconst stream = client.messages.stream({\n\t\t\t\t\tmodel: opts.model,\n\t\t\t\t\tmax_tokens: req.maxTokens ?? 8192,\n\t\t\t\t\t...req.systemPrompt !== void 0 ? { system: req.systemPrompt } : {},\n\t\t\t\t\tmessages: req.messages.filter((m) => m.role !== \"system\").map((m) => ({\n\t\t\t\t\t\trole: m.role,\n\t\t\t\t\t\tcontent: m.content\n\t\t\t\t\t}))\n\t\t\t\t});\n\t\t\t\tfor await (const event of stream) if (event.type === \"content_block_delta\" && event.delta.type === \"text_delta\") yield {\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\tdelta: event.delta.text\n\t\t\t\t};\n\t\t\t\tconst final = await stream.finalMessage();\n\t\t\t\tyield {\n\t\t\t\t\ttype: \"end\",\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinputTokens: final.usage.input_tokens,\n\t\t\t\t\t\toutputTokens: final.usage.output_tokens\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t} catch (e) {\n\t\t\t\tyield {\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\tmessage: String(e)\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t};\n}\n\n//#endregion\n//#region src/adapters/ollama.ts\nfunction createOllamaAdapter(opts) {\n\tconst baseUrl = opts.baseUrl ?? \"http://127.0.0.1:11434\";\n\treturn {\n\t\tid: `ollama:${opts.model}`,\n\t\tasync *stream(req) {\n\t\t\ttry {\n\t\t\t\tconst messages = req.systemPrompt ? [{\n\t\t\t\t\trole: \"system\",\n\t\t\t\t\tcontent: req.systemPrompt\n\t\t\t\t}, ...req.messages] : req.messages;\n\t\t\t\tconst res = await fetch(`${baseUrl}/api/chat`, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tmodel: opts.model,\n\t\t\t\t\t\tmessages,\n\t\t\t\t\t\tstream: true\n\t\t\t\t\t})\n\t\t\t\t});\n\t\t\t\tif (!res.ok || !res.body) {\n\t\t\t\t\tyield {\n\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\tmessage: `Ollama HTTP ${res.status}`\n\t\t\t\t\t};\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst reader = res.body.getReader();\n\t\t\t\tconst decoder = new TextDecoder();\n\t\t\t\tlet inputTokens = 0;\n\t\t\t\tlet outputTokens = 0;\n\t\t\t\twhile (true) {\n\t\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\t\tif (done) break;\n\t\t\t\t\tfor (const line of decoder.decode(value).split(\"\\n\")) {\n\t\t\t\t\t\tif (!line.trim()) continue;\n\t\t\t\t\t\tconst parsed = JSON.parse(line);\n\t\t\t\t\t\tconst msg = parsed[\"message\"];\n\t\t\t\t\t\tif (msg?.[\"content\"]) yield {\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\tdelta: String(msg[\"content\"])\n\t\t\t\t\t\t};\n\t\t\t\t\t\tif (parsed[\"done\"]) {\n\t\t\t\t\t\t\tinputTokens = Number(parsed[\"prompt_eval_count\"] ?? 0);\n\t\t\t\t\t\t\toutputTokens = Number(parsed[\"eval_count\"] ?? 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tyield {\n\t\t\t\t\ttype: \"end\",\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinputTokens,\n\t\t\t\t\t\toutputTokens\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t} catch (e) {\n\t\t\t\tyield {\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\tmessage: String(e)\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t};\n}\n\n//#endregion\n//#region src/adapters/openai.ts\nfunction createOpenAIAdapter(opts) {\n\tconst client = new OpenAI({ apiKey: opts.apiKey });\n\treturn {\n\t\tid: `openai:${opts.model}`,\n\t\tprovider: \"openai\",\n\t\tasync *stream(req) {\n\t\t\ttry {\n\t\t\t\tconst messages = [];\n\t\t\t\tif (req.systemPrompt) messages.push({\n\t\t\t\t\trole: \"system\",\n\t\t\t\t\tcontent: req.systemPrompt\n\t\t\t\t});\n\t\t\t\tfor (const m of req.messages) {\n\t\t\t\t\tif (m.role === \"system\") continue;\n\t\t\t\t\tmessages.push({\n\t\t\t\t\t\trole: m.role,\n\t\t\t\t\t\tcontent: m.content\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst stream = await client.chat.completions.create({\n\t\t\t\t\tmodel: opts.model,\n\t\t\t\t\tmessages,\n\t\t\t\t\tmax_tokens: req.maxTokens ?? 8192,\n\t\t\t\t\tstream: true\n\t\t\t\t});\n\t\t\t\tlet inputTokens = 0;\n\t\t\t\tlet outputTokens = 0;\n\t\t\t\tfor await (const chunk of stream) {\n\t\t\t\t\tconst delta = chunk.choices[0]?.delta?.content;\n\t\t\t\t\tif (delta) yield {\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\tdelta\n\t\t\t\t\t};\n\t\t\t\t\tif (chunk.usage) {\n\t\t\t\t\t\tinputTokens = chunk.usage.prompt_tokens;\n\t\t\t\t\t\toutputTokens = chunk.usage.completion_tokens;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tyield {\n\t\t\t\t\ttype: \"end\",\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinputTokens,\n\t\t\t\t\t\toutputTokens\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t} catch (e) {\n\t\t\t\tyield {\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\tmessage: String(e)\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t};\n}\n\n//#endregion\nexport { createAnthropicAdapter, createLLMRouter, createOllamaAdapter, createOpenAIAdapter };\n//# sourceMappingURL=index.js.map","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { mkdirSync } from 'node:fs';\nimport { createGatewayServer, loadConfig, createDispatcher } from '@lovely/core';\nimport {\n createAnthropicAdapter,\n createOpenAIAdapter,\n createOllamaAdapter,\n createLLMRouter,\n} from '@lovely/llm';\nimport type { LLMAdapter } from '@lovely/llm';\nimport type { ChannelPlugin } from '@lovely/channels';\nimport { createWhatsAppPlugin } from '@lovely/channel-whatsapp';\nimport { createTelegramPlugin } from '@lovely/channel-telegram';\nimport { createIMessagePlugin } from '@lovely/channel-imessage';\n\nfunction buildLLMAdapter(config: ReturnType<typeof loadConfig>): LLMAdapter {\n const { primary, fallbacks, providers } = config.llm;\n\n function makeAdapter(provider: string, model: string): LLMAdapter {\n const provCfg = providers[provider] ?? {};\n if (provider === 'anthropic') {\n if (!provCfg.apiKey) throw new Error('Anthropic apiKey missing in config');\n return createAnthropicAdapter({ apiKey: provCfg.apiKey, model });\n }\n if (provider === 'openai') {\n if (!provCfg.apiKey) throw new Error('OpenAI apiKey missing in config');\n return createOpenAIAdapter({ apiKey: provCfg.apiKey, model });\n }\n if (provider === 'ollama') {\n return createOllamaAdapter({ baseUrl: provCfg.baseUrl ?? 'http://localhost:11434', model });\n }\n throw new Error(`Unknown LLM provider: ${provider}`);\n }\n\n const primaryAdapter = makeAdapter(primary.provider, primary.model);\n const fallbackAdapters = fallbacks.map((f) => makeAdapter(f.provider, f.model));\n\n return createLLMRouter({ primary: primaryAdapter, fallbacks: fallbackAdapters });\n}\n\nasync function startChannels(\n config: ReturnType<typeof loadConfig>,\n onMessage: ChannelPlugin['start'] extends (opts: infer O) => unknown ? O['onMessage'] : never,\n): Promise<ChannelPlugin[]> {\n const channels = config.channels;\n const started: ChannelPlugin[] = [];\n const home = homedir();\n\n if (channels['whatsapp']?.enabled) {\n const authDir = (channels['whatsapp'].authDir ?? '~/.lovely/whatsapp-auth').replace('~', home);\n mkdirSync(authDir, { recursive: true });\n const plugin = createWhatsAppPlugin({\n authDir,\n accountId: channels['whatsapp'].accountId ?? 'default',\n });\n await plugin.start({ onMessage, config: channels['whatsapp'] as Record<string, unknown> });\n started.push(plugin);\n console.log(chalk.dim(' WhatsApp channel started'));\n }\n\n if (channels['telegram']?.enabled) {\n const botToken = channels['telegram'].botToken;\n if (!botToken) throw new Error('Telegram botToken missing in config');\n const plugin = createTelegramPlugin({ botToken });\n await plugin.start({ onMessage, config: channels['telegram'] as Record<string, unknown> });\n started.push(plugin);\n console.log(chalk.dim(' Telegram channel started'));\n }\n\n if (channels['imessage']?.enabled) {\n const plugin = createIMessagePlugin({});\n await plugin.start({ onMessage, config: channels['imessage'] as Record<string, unknown> });\n started.push(plugin);\n console.log(chalk.dim(' iMessage channel started'));\n }\n\n return started;\n}\n\nexport function gatewayCommand(): Command {\n return new Command('gateway')\n .description('Start the Lovely gateway daemon')\n .option('-p, --port <port>', 'Port to listen on', '18900')\n .option('--verbose', 'Enable verbose logging')\n .action(async (opts: { port: string; verbose?: boolean }) => {\n const config = loadConfig();\n const port = parseInt(opts.port, 10);\n\n if (Number.isNaN(port) || port < 1 || port > 65535) {\n console.error(chalk.red(`Invalid port: ${String(opts.port)}`));\n process.exit(1);\n }\n\n const spinner = ora(`Starting Lovely gateway on port ${port}...`).start();\n\n let channels: ChannelPlugin[] = [];\n\n try {\n // 1. Build LLM adapter from config\n const llm = buildLLMAdapter(config);\n\n // 2. Create session store dir\n const sessionStoreDir = join(homedir(), '.lovely', 'sessions');\n mkdirSync(sessionStoreDir, { recursive: true });\n\n // 3. Create dispatcher (Privacy Gate + Agent Runner)\n const dispatcher = createDispatcher({\n privacy: config.privacy,\n hitl: config.hitl,\n llm,\n sessionStoreDir,\n onReply: async ({ to, channel: channelId, text }) => {\n const ch = channels.find((c) => c.id === channelId);\n if (ch) {\n await ch.send(to, { text });\n } else if (opts.verbose) {\n console.log(chalk.dim(`[reply] no channel for id=${channelId}`));\n }\n },\n });\n\n // 4. Start HTTP/WS gateway server\n const server = await createGatewayServer({ port, host: config.gateway.host });\n\n spinner.succeed(\n chalk.green(`Lovely gateway running on ${chalk.bold(`http://127.0.0.1:${port}`)}`),\n );\n\n // 5. Start enabled channel plugins\n channels = await startChannels(config, (ctx) => dispatcher.dispatch(ctx));\n\n const enabledChannels = Object.entries(config.channels)\n .filter(([, c]) => c.enabled)\n .map(([id]) => id);\n\n if (enabledChannels.length === 0) {\n console.log(chalk.yellow(' No channels enabled. Edit ~/.lovely/lovely.json to add channels.'));\n }\n\n console.log(chalk.dim(' Press Ctrl+C to stop'));\n\n const shutdown = async (): Promise<void> => {\n console.log('\\n' + chalk.yellow('Shutting down...'));\n await Promise.allSettled(channels.map((c) => c.stop()));\n await server.stop();\n process.exit(0);\n };\n\n process.on('SIGINT', () => { void shutdown(); });\n process.on('SIGTERM', () => { void shutdown(); });\n } catch (err) {\n spinner.fail(chalk.red(`Failed to start gateway: ${err}`));\n process.exit(1);\n }\n });\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { confirm, input, select } from '@inquirer/prompts';\nimport { writeFileSync, mkdirSync, chmodSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { execFileSync } from 'node:child_process';\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nexport function onboardCommand(): Command {\n return new Command('onboard')\n .description('Interactive setup wizard for Lovely')\n .option('--install-daemon', 'Install launchd daemon after setup')\n .action(async (opts: { installDaemon?: boolean }) => {\n console.log(chalk.bold.magenta('\\nWelcome to Lovely setup\\n'));\n\n const configDir = join(homedir(), '.lovely');\n mkdirSync(configDir, { recursive: true });\n\n // Step 1: LLM provider\n const provider = await select({\n message: 'Which AI provider do you want to use?',\n choices: [\n { name: 'Anthropic (Claude) — recommended', value: 'anthropic' },\n { name: 'Ollama (local, privacy-first)', value: 'ollama' },\n { name: 'OpenAI', value: 'openai' },\n { name: 'Skip for now', value: 'skip' },\n ],\n });\n\n let apiKey = '';\n let model = '';\n\n if (provider === 'anthropic') {\n apiKey = await input({\n message: 'Anthropic API key:',\n validate: (v) => v.startsWith('sk-') || 'Must start with sk-',\n });\n model = 'claude-sonnet-4-6';\n } else if (provider === 'openai') {\n apiKey = await input({\n message: 'OpenAI API key:',\n validate: (v) => v.startsWith('sk-') || 'Must start with sk-',\n });\n model = 'gpt-4o';\n } else if (provider === 'ollama') {\n model = await input({ message: 'Ollama model name:', default: 'llama3.2' });\n }\n\n // Step 2: Privacy policy\n const privacy = await select({\n message: 'Default privacy policy:',\n choices: [\n { name: 'Local-first (cloud only if explicitly allowed)', value: 'local-first' },\n { name: 'Local-only (never send to cloud)', value: 'local-only' },\n { name: 'Cloud-allowed (use cloud freely)', value: 'cloud-allowed' },\n ],\n });\n\n // Step 3: iMessage\n const enableIMessage = await confirm({ message: 'Enable iMessage channel?', default: true });\n\n // Write config\n const config = {\n gateway: { port: 18900 },\n privacy: {\n default: privacy,\n cloudAllowed: privacy !== 'local-only' && provider !== 'skip' ? [provider] : [],\n },\n hitl: { enabled: true },\n llm: {\n primary: { provider, model },\n providers:\n provider !== 'ollama' && provider !== 'skip'\n ? { [provider]: { apiKey, type: provider } }\n : {},\n },\n channels: {\n imessage: { enabled: enableIMessage },\n },\n };\n\n const configFilePath = join(configDir, 'lovely.json');\n writeFileSync(configFilePath, JSON.stringify(config, null, 2), { mode: 0o600 });\n chmodSync(configFilePath, 0o600);\n console.log(chalk.green(`\\n✓ Config written to ${configDir}/lovely.json (permissions: 600)`));\n\n if (opts.installDaemon === true) {\n installLaunchdDaemon();\n }\n\n console.log(chalk.bold('\\nSetup complete! Run: ') + chalk.cyan('lovely gateway') + '\\n');\n });\n}\n\nfunction installLaunchdDaemon(): void {\n if (process.platform !== 'darwin') {\n console.error(chalk.red('--install-daemon is only supported on macOS'));\n process.exit(1);\n }\n\n const binPath = process.argv[1] ?? 'lovely';\n\n const plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>ai.lovely.gateway</string>\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(process.execPath)}</string>\n <string>${escapeXml(binPath)}</string>\n <string>gateway</string>\n </array>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <true/>\n <key>StandardOutPath</key>\n <string>${homedir()}/.lovely/logs/gateway.log</string>\n <key>StandardErrorPath</key>\n <string>${homedir()}/.lovely/logs/gateway.error.log</string>\n</dict>\n</plist>`;\n\n const launchAgentsDir = join(homedir(), 'Library', 'LaunchAgents');\n mkdirSync(launchAgentsDir, { recursive: true });\n mkdirSync(join(homedir(), '.lovely', 'logs'), { recursive: true });\n\n const plistPath = join(launchAgentsDir, 'ai.lovely.gateway.plist');\n writeFileSync(plistPath, plist);\n\n execFileSync('launchctl', ['load', plistPath]);\n console.log(chalk.green('Launchd daemon installed — Lovely will start automatically on login'));\n}\n","import { Command } from 'commander';\nimport { gatewayCommand } from './commands/gateway.js';\nimport { onboardCommand } from './commands/onboard.js';\n\nconst program = new Command();\n\nprogram\n .name('lovely')\n .description('Lovely — your personal AGI. Runs on your hardware.')\n .version(process.env['npm_package_version'] ?? '0.0.1');\n\nprogram.addCommand(gatewayCommand());\nprogram.addCommand(onboardCommand());\n\nprogram.parse(process.argv);\n"],"mappings":"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"}
|