freeturtle 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/LICENSE +190 -0
- package/README.md +392 -0
- package/dist/bin/freeturtle.d.ts +2 -0
- package/dist/bin/freeturtle.js +119 -0
- package/dist/bin/freeturtle.js.map +1 -0
- package/dist/src/approval.d.ts +38 -0
- package/dist/src/approval.js +140 -0
- package/dist/src/approval.js.map +1 -0
- package/dist/src/audit.d.ts +33 -0
- package/dist/src/audit.js +36 -0
- package/dist/src/audit.js.map +1 -0
- package/dist/src/channels/telegram.d.ts +10 -0
- package/dist/src/channels/telegram.js +41 -0
- package/dist/src/channels/telegram.js.map +1 -0
- package/dist/src/channels/terminal.d.ts +9 -0
- package/dist/src/channels/terminal.js +52 -0
- package/dist/src/channels/terminal.js.map +1 -0
- package/dist/src/channels/types.d.ts +6 -0
- package/dist/src/channels/types.js +2 -0
- package/dist/src/channels/types.js.map +1 -0
- package/dist/src/cli/approvals.d.ts +3 -0
- package/dist/src/cli/approvals.js +33 -0
- package/dist/src/cli/approvals.js.map +1 -0
- package/dist/src/cli/connect-farcaster.d.ts +5 -0
- package/dist/src/cli/connect-farcaster.js +265 -0
- package/dist/src/cli/connect-farcaster.js.map +1 -0
- package/dist/src/cli/connection-tests.d.ts +16 -0
- package/dist/src/cli/connection-tests.js +65 -0
- package/dist/src/cli/connection-tests.js.map +1 -0
- package/dist/src/cli/init.d.ts +1 -0
- package/dist/src/cli/init.js +729 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/install-service.d.ts +1 -0
- package/dist/src/cli/install-service.js +57 -0
- package/dist/src/cli/install-service.js.map +1 -0
- package/dist/src/cli/intake.d.ts +23 -0
- package/dist/src/cli/intake.js +68 -0
- package/dist/src/cli/intake.js.map +1 -0
- package/dist/src/cli/send.d.ts +1 -0
- package/dist/src/cli/send.js +16 -0
- package/dist/src/cli/send.js.map +1 -0
- package/dist/src/cli/start.d.ts +3 -0
- package/dist/src/cli/start.js +25 -0
- package/dist/src/cli/start.js.map +1 -0
- package/dist/src/cli/status.d.ts +2 -0
- package/dist/src/cli/status.js +54 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/cli/update.d.ts +1 -0
- package/dist/src/cli/update.js +39 -0
- package/dist/src/cli/update.js.map +1 -0
- package/dist/src/config.d.ts +31 -0
- package/dist/src/config.js +93 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/daemon.d.ts +18 -0
- package/dist/src/daemon.js +272 -0
- package/dist/src/daemon.js.map +1 -0
- package/dist/src/heartbeat.d.ts +17 -0
- package/dist/src/heartbeat.js +60 -0
- package/dist/src/heartbeat.js.map +1 -0
- package/dist/src/llm.d.ts +29 -0
- package/dist/src/llm.js +225 -0
- package/dist/src/llm.js.map +1 -0
- package/dist/src/logger.d.ts +8 -0
- package/dist/src/logger.js +45 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/memory.d.ts +3 -0
- package/dist/src/memory.js +36 -0
- package/dist/src/memory.js.map +1 -0
- package/dist/src/modules/database/client.d.ts +18 -0
- package/dist/src/modules/database/client.js +50 -0
- package/dist/src/modules/database/client.js.map +1 -0
- package/dist/src/modules/database/index.d.ts +9 -0
- package/dist/src/modules/database/index.js +32 -0
- package/dist/src/modules/database/index.js.map +1 -0
- package/dist/src/modules/database/tools.d.ts +2 -0
- package/dist/src/modules/database/tools.js +26 -0
- package/dist/src/modules/database/tools.js.map +1 -0
- package/dist/src/modules/farcaster/client.d.ts +43 -0
- package/dist/src/modules/farcaster/client.js +87 -0
- package/dist/src/modules/farcaster/client.js.map +1 -0
- package/dist/src/modules/farcaster/index.d.ts +14 -0
- package/dist/src/modules/farcaster/index.js +71 -0
- package/dist/src/modules/farcaster/index.js.map +1 -0
- package/dist/src/modules/farcaster/tools.d.ts +2 -0
- package/dist/src/modules/farcaster/tools.js +90 -0
- package/dist/src/modules/farcaster/tools.js.map +1 -0
- package/dist/src/modules/github/client.d.ts +21 -0
- package/dist/src/modules/github/client.js +80 -0
- package/dist/src/modules/github/client.js.map +1 -0
- package/dist/src/modules/github/index.d.ts +13 -0
- package/dist/src/modules/github/index.js +45 -0
- package/dist/src/modules/github/index.js.map +1 -0
- package/dist/src/modules/github/tools.d.ts +2 -0
- package/dist/src/modules/github/tools.js +74 -0
- package/dist/src/modules/github/tools.js.map +1 -0
- package/dist/src/modules/loader.d.ts +5 -0
- package/dist/src/modules/loader.js +35 -0
- package/dist/src/modules/loader.js.map +1 -0
- package/dist/src/modules/onchain/client.d.ts +8 -0
- package/dist/src/modules/onchain/client.js +46 -0
- package/dist/src/modules/onchain/client.js.map +1 -0
- package/dist/src/modules/onchain/index.d.ts +13 -0
- package/dist/src/modules/onchain/index.js +40 -0
- package/dist/src/modules/onchain/index.js.map +1 -0
- package/dist/src/modules/onchain/tools.d.ts +2 -0
- package/dist/src/modules/onchain/tools.js +61 -0
- package/dist/src/modules/onchain/tools.js.map +1 -0
- package/dist/src/modules/types.d.ts +24 -0
- package/dist/src/modules/types.js +2 -0
- package/dist/src/modules/types.js.map +1 -0
- package/dist/src/modules/xmtp/index.d.ts +8 -0
- package/dist/src/modules/xmtp/index.js +14 -0
- package/dist/src/modules/xmtp/index.js.map +1 -0
- package/dist/src/policy.d.ts +45 -0
- package/dist/src/policy.js +164 -0
- package/dist/src/policy.js.map +1 -0
- package/dist/src/redaction.d.ts +13 -0
- package/dist/src/redaction.js +75 -0
- package/dist/src/redaction.js.map +1 -0
- package/dist/src/reliability.d.ts +16 -0
- package/dist/src/reliability.js +124 -0
- package/dist/src/reliability.js.map +1 -0
- package/dist/src/runner.d.ts +37 -0
- package/dist/src/runner.js +257 -0
- package/dist/src/runner.js.map +1 -0
- package/dist/src/scheduler.d.ts +22 -0
- package/dist/src/scheduler.js +61 -0
- package/dist/src/scheduler.js.map +1 -0
- package/dist/src/setup.d.ts +8 -0
- package/dist/src/setup.js +179 -0
- package/dist/src/setup.js.map +1 -0
- package/dist/src/soul.d.ts +1 -0
- package/dist/src/soul.js +15 -0
- package/dist/src/soul.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { writeFile, unlink } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import net from "node:net";
|
|
4
|
+
import { config as loadDotenv } from "dotenv";
|
|
5
|
+
import { loadConfig } from "./config.js";
|
|
6
|
+
import { LLMClient } from "./llm.js";
|
|
7
|
+
import { loadModules } from "./modules/loader.js";
|
|
8
|
+
import { TaskRunner } from "./runner.js";
|
|
9
|
+
import { Scheduler } from "./scheduler.js";
|
|
10
|
+
import { Heartbeat } from "./heartbeat.js";
|
|
11
|
+
import { TerminalChannel } from "./channels/terminal.js";
|
|
12
|
+
import { TelegramChannel } from "./channels/telegram.js";
|
|
13
|
+
import { createLogger } from "./logger.js";
|
|
14
|
+
export class FreeTurtleDaemon {
|
|
15
|
+
dir;
|
|
16
|
+
options;
|
|
17
|
+
logger;
|
|
18
|
+
scheduler;
|
|
19
|
+
heartbeat;
|
|
20
|
+
channels = [];
|
|
21
|
+
runner;
|
|
22
|
+
ipcServer;
|
|
23
|
+
constructor(dir, options = {}) {
|
|
24
|
+
this.dir = dir;
|
|
25
|
+
this.options = options;
|
|
26
|
+
this.logger = createLogger(dir);
|
|
27
|
+
}
|
|
28
|
+
async start() {
|
|
29
|
+
// Load .env from workspace
|
|
30
|
+
loadDotenv({ path: join(this.dir, ".env") });
|
|
31
|
+
const env = process.env;
|
|
32
|
+
// Load config
|
|
33
|
+
const config = await loadConfig(this.dir);
|
|
34
|
+
this.logger.info("Config loaded");
|
|
35
|
+
// Create LLM client
|
|
36
|
+
const provider = (config.llm.provider ?? "claude_api");
|
|
37
|
+
const isOAuth = provider.endsWith("subscription");
|
|
38
|
+
const credEnvName = isOAuth
|
|
39
|
+
? config.llm.oauth_token_env
|
|
40
|
+
: config.llm.api_key_env;
|
|
41
|
+
const credField = isOAuth ? "oauthToken" : "apiKey";
|
|
42
|
+
// Try config-specified env var first, then fall back to well-known names
|
|
43
|
+
const FALLBACK_ENV = {
|
|
44
|
+
claude_api: "ANTHROPIC_API_KEY",
|
|
45
|
+
claude_subscription: "ANTHROPIC_AUTH_TOKEN",
|
|
46
|
+
openai_api: "OPENAI_API_KEY",
|
|
47
|
+
openai_subscription: "OPENAI_OAUTH_TOKEN",
|
|
48
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
49
|
+
};
|
|
50
|
+
const credential = (credEnvName ? env[credEnvName] : undefined) ??
|
|
51
|
+
env[FALLBACK_ENV[provider]];
|
|
52
|
+
if (!credential) {
|
|
53
|
+
throw new Error(`Missing credential: set ${credEnvName ?? FALLBACK_ENV[provider]} in .env`);
|
|
54
|
+
}
|
|
55
|
+
const llm = new LLMClient({
|
|
56
|
+
provider,
|
|
57
|
+
model: config.llm.model,
|
|
58
|
+
[credField]: credential,
|
|
59
|
+
baseUrl: config.llm.base_url,
|
|
60
|
+
});
|
|
61
|
+
this.logger.info(`LLM: ${provider} / ${config.llm.model}`);
|
|
62
|
+
// Load modules (pass policy for allowlist enforcement)
|
|
63
|
+
const modules = await loadModules(config, env, this.logger, config.policy);
|
|
64
|
+
this.logger.info(`Modules: ${modules.map((m) => m.name).join(", ") || "none"}`);
|
|
65
|
+
// Create runner with policy and approval notifications
|
|
66
|
+
this.runner = new TaskRunner(this.dir, llm, modules, this.logger, {
|
|
67
|
+
policy: config.policy,
|
|
68
|
+
onApprovalNeeded: (msg) => {
|
|
69
|
+
this.logger.info(`Approval notification: ${msg.slice(0, 100)}`);
|
|
70
|
+
for (const ch of this.channels) {
|
|
71
|
+
ch.send(msg).catch((err) => {
|
|
72
|
+
this.logger.error(`Failed to send approval notification via ${ch.name}: ${err}`);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
// Start scheduler
|
|
78
|
+
if (Object.keys(config.cron).length > 0) {
|
|
79
|
+
this.scheduler = new Scheduler(config.cron, this.runner, this.logger);
|
|
80
|
+
this.scheduler.start();
|
|
81
|
+
}
|
|
82
|
+
// Start heartbeat — send alerts to all active channels
|
|
83
|
+
this.heartbeat = new Heartbeat(this.runner, this.logger, {
|
|
84
|
+
onAlert: (msg) => {
|
|
85
|
+
for (const ch of this.channels) {
|
|
86
|
+
ch.send(msg).catch((err) => {
|
|
87
|
+
this.logger.error(`Failed to send alert via ${ch.name}: ${err}`);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
this.heartbeat.start();
|
|
93
|
+
// Start channels
|
|
94
|
+
const onMessage = async (text) => {
|
|
95
|
+
return this.runner.runMessage(text, "channel");
|
|
96
|
+
};
|
|
97
|
+
if (this.options.chat) {
|
|
98
|
+
const terminal = new TerminalChannel();
|
|
99
|
+
this.channels.push(terminal);
|
|
100
|
+
await terminal.start(onMessage);
|
|
101
|
+
}
|
|
102
|
+
if (config.channels.telegram?.enabled) {
|
|
103
|
+
const token = env.TELEGRAM_BOT_TOKEN;
|
|
104
|
+
const ownerId = env.TELEGRAM_OWNER_ID;
|
|
105
|
+
if (token && ownerId) {
|
|
106
|
+
const telegram = new TelegramChannel(token, parseInt(ownerId, 10));
|
|
107
|
+
this.channels.push(telegram);
|
|
108
|
+
await telegram.start(onMessage);
|
|
109
|
+
this.logger.info("Telegram channel started");
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.logger.warn("Telegram enabled but TELEGRAM_BOT_TOKEN or TELEGRAM_OWNER_ID missing");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Write PID file
|
|
116
|
+
const pidPath = join(this.dir, "daemon.pid");
|
|
117
|
+
await writeFile(pidPath, String(process.pid), "utf-8");
|
|
118
|
+
// Start IPC server
|
|
119
|
+
await this.startIpc();
|
|
120
|
+
// Signal handlers
|
|
121
|
+
const shutdown = async () => {
|
|
122
|
+
this.logger.info("Shutting down...");
|
|
123
|
+
await this.stop();
|
|
124
|
+
process.exit(0);
|
|
125
|
+
};
|
|
126
|
+
process.on("SIGINT", () => void shutdown());
|
|
127
|
+
process.on("SIGTERM", () => void shutdown());
|
|
128
|
+
process.on("uncaughtException", (err) => {
|
|
129
|
+
this.logger.error(`Uncaught exception: ${err.message}`);
|
|
130
|
+
});
|
|
131
|
+
this.logger.info("FreeTurtle is running");
|
|
132
|
+
const moduleNames = modules.map((m) => m.name).join(", ") || "none";
|
|
133
|
+
const channelNames = this.channels.map((c) => c.name).join(", ") || "none";
|
|
134
|
+
const cronCount = Object.keys(config.cron).length;
|
|
135
|
+
console.log(`
|
|
136
|
+
\x1b[38;2;94;255;164m _____ ____\x1b[0m
|
|
137
|
+
\x1b[38;2;94;255;164m / \\ | o |\x1b[0m
|
|
138
|
+
\x1b[38;2;94;255;164m| |/ ___\\|\x1b[0m
|
|
139
|
+
\x1b[38;2;94;255;164m|_________/\x1b[0m
|
|
140
|
+
\x1b[38;2;94;255;164m|_|_| |_|_|\x1b[0m
|
|
141
|
+
|
|
142
|
+
\x1b[1mPID ${process.pid}\x1b[0m — swimming along
|
|
143
|
+
|
|
144
|
+
Modules ${moduleNames}
|
|
145
|
+
Cron tasks ${cronCount}
|
|
146
|
+
Channels ${channelNames}
|
|
147
|
+
|
|
148
|
+
\x1b[2mfreeturtle send "message" — talk to your CEO\x1b[0m
|
|
149
|
+
\x1b[2mfreeturtle start --chat — interactive mode\x1b[0m
|
|
150
|
+
\x1b[2mfreeturtle status — check on things\x1b[0m
|
|
151
|
+
\x1b[2mCtrl+C — stop\x1b[0m
|
|
152
|
+
`);
|
|
153
|
+
}
|
|
154
|
+
async stop() {
|
|
155
|
+
this.scheduler?.stop();
|
|
156
|
+
this.heartbeat?.stop();
|
|
157
|
+
for (const ch of this.channels) {
|
|
158
|
+
await ch.stop();
|
|
159
|
+
}
|
|
160
|
+
this.ipcServer?.close();
|
|
161
|
+
// Remove PID file
|
|
162
|
+
try {
|
|
163
|
+
await unlink(join(this.dir, "daemon.pid"));
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// ignore
|
|
167
|
+
}
|
|
168
|
+
// Remove socket
|
|
169
|
+
try {
|
|
170
|
+
await unlink(join(this.dir, "daemon.sock"));
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// ignore
|
|
174
|
+
}
|
|
175
|
+
this.logger.info("FreeTurtle stopped");
|
|
176
|
+
}
|
|
177
|
+
async startIpc() {
|
|
178
|
+
const sockPath = join(this.dir, "daemon.sock");
|
|
179
|
+
// Remove stale socket
|
|
180
|
+
try {
|
|
181
|
+
await unlink(sockPath);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// ignore
|
|
185
|
+
}
|
|
186
|
+
this.ipcServer = net.createServer((conn) => {
|
|
187
|
+
let data = "";
|
|
188
|
+
conn.on("data", (chunk) => {
|
|
189
|
+
data += chunk.toString();
|
|
190
|
+
});
|
|
191
|
+
conn.on("end", () => {
|
|
192
|
+
void this.handleIpc(data.trim()).then((response) => {
|
|
193
|
+
conn.write(response);
|
|
194
|
+
conn.end();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
this.ipcServer.listen(sockPath);
|
|
199
|
+
this.logger.info(`IPC listening on ${sockPath}`);
|
|
200
|
+
}
|
|
201
|
+
async handleIpc(command) {
|
|
202
|
+
if (command === "status") {
|
|
203
|
+
const status = {
|
|
204
|
+
pid: process.pid,
|
|
205
|
+
uptime: process.uptime(),
|
|
206
|
+
scheduler: this.scheduler?.getStatus() ?? null,
|
|
207
|
+
channels: this.channels.map((c) => c.name),
|
|
208
|
+
};
|
|
209
|
+
return JSON.stringify(status, null, 2);
|
|
210
|
+
}
|
|
211
|
+
if (command.startsWith("send ")) {
|
|
212
|
+
const message = command.slice(5);
|
|
213
|
+
if (!this.runner)
|
|
214
|
+
return "Error: runner not initialized";
|
|
215
|
+
try {
|
|
216
|
+
const response = await this.runner.runMessage(message, "ipc");
|
|
217
|
+
return response;
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
221
|
+
this.logger.error(`IPC send failed: ${msg}`);
|
|
222
|
+
return `Error: ${msg}`;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (command.startsWith("approve ")) {
|
|
226
|
+
const id = command.slice(8).trim();
|
|
227
|
+
if (!this.runner)
|
|
228
|
+
return "Error: runner not initialized";
|
|
229
|
+
try {
|
|
230
|
+
const req = await this.runner.getApprovalManager().approve(id, "ipc");
|
|
231
|
+
return JSON.stringify(req, null, 2);
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
return `Error: ${err instanceof Error ? err.message : "unknown"}`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (command.startsWith("reject ")) {
|
|
238
|
+
const parts = command.slice(7).trim();
|
|
239
|
+
const spaceIdx = parts.indexOf(" ");
|
|
240
|
+
const id = spaceIdx > -1 ? parts.slice(0, spaceIdx) : parts;
|
|
241
|
+
const reason = spaceIdx > -1 ? parts.slice(spaceIdx + 1) : undefined;
|
|
242
|
+
if (!this.runner)
|
|
243
|
+
return "Error: runner not initialized";
|
|
244
|
+
try {
|
|
245
|
+
const req = await this.runner.getApprovalManager().reject(id, reason, "ipc");
|
|
246
|
+
return JSON.stringify(req, null, 2);
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
return `Error: ${err instanceof Error ? err.message : "unknown"}`;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (command === "approvals") {
|
|
253
|
+
if (!this.runner)
|
|
254
|
+
return "Error: runner not initialized";
|
|
255
|
+
try {
|
|
256
|
+
const pending = await this.runner.getApprovalManager().list("pending");
|
|
257
|
+
if (pending.length === 0)
|
|
258
|
+
return "No pending approvals.";
|
|
259
|
+
return JSON.stringify(pending, null, 2);
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
return `Error: ${err instanceof Error ? err.message : "unknown"}`;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (command === "stop") {
|
|
266
|
+
void this.stop().then(() => process.exit(0));
|
|
267
|
+
return "Stopping...";
|
|
268
|
+
}
|
|
269
|
+
return `Unknown command: ${command}`;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TaskRunner } from "./runner.js";
|
|
2
|
+
import type { Logger } from "./logger.js";
|
|
3
|
+
export declare class Heartbeat {
|
|
4
|
+
private interval;
|
|
5
|
+
private timer;
|
|
6
|
+
private runner;
|
|
7
|
+
private logger;
|
|
8
|
+
private onAlert?;
|
|
9
|
+
private running;
|
|
10
|
+
constructor(runner: TaskRunner, logger: Logger, options?: {
|
|
11
|
+
intervalMs?: number;
|
|
12
|
+
onAlert?: (message: string) => void;
|
|
13
|
+
});
|
|
14
|
+
start(): void;
|
|
15
|
+
stop(): void;
|
|
16
|
+
private tick;
|
|
17
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export class Heartbeat {
|
|
2
|
+
interval;
|
|
3
|
+
timer = null;
|
|
4
|
+
runner;
|
|
5
|
+
logger;
|
|
6
|
+
onAlert;
|
|
7
|
+
running = false;
|
|
8
|
+
constructor(runner, logger, options) {
|
|
9
|
+
this.runner = runner;
|
|
10
|
+
this.logger = logger;
|
|
11
|
+
this.interval = options?.intervalMs ?? 30 * 60 * 1000;
|
|
12
|
+
this.onAlert = options?.onAlert;
|
|
13
|
+
}
|
|
14
|
+
start() {
|
|
15
|
+
this.logger.info(`Heartbeat started (every ${Math.round(this.interval / 1000)}s)`);
|
|
16
|
+
this.timer = setInterval(() => {
|
|
17
|
+
void this.tick();
|
|
18
|
+
}, this.interval);
|
|
19
|
+
}
|
|
20
|
+
stop() {
|
|
21
|
+
if (this.timer) {
|
|
22
|
+
clearInterval(this.timer);
|
|
23
|
+
this.timer = null;
|
|
24
|
+
}
|
|
25
|
+
this.logger.info("Heartbeat stopped");
|
|
26
|
+
}
|
|
27
|
+
async tick() {
|
|
28
|
+
if (this.running) {
|
|
29
|
+
this.logger.warn("Skipping heartbeat — previous tick still running");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.running = true;
|
|
33
|
+
try {
|
|
34
|
+
this.logger.info("Heartbeat tick");
|
|
35
|
+
const result = await this.runner.runTask({
|
|
36
|
+
name: "heartbeat",
|
|
37
|
+
prompt: "This is a scheduled heartbeat check. Review your heartbeat checklist. " +
|
|
38
|
+
"If everything looks fine, respond with exactly HEARTBEAT_OK. " +
|
|
39
|
+
"If something needs attention, describe what you found.",
|
|
40
|
+
isHeartbeat: true,
|
|
41
|
+
});
|
|
42
|
+
const text = result.response.trim();
|
|
43
|
+
if (text === "HEARTBEAT_OK" || text.includes("HEARTBEAT_OK")) {
|
|
44
|
+
this.logger.info("Heartbeat: all clear");
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this.logger.info("Heartbeat: alert raised");
|
|
48
|
+
this.onAlert?.(text);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
53
|
+
this.logger.error(`Heartbeat failed: ${msg}`);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
this.running = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/heartbeat.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,SAAS;IACZ,QAAQ,CAAS;IACjB,KAAK,GAA0C,IAAI,CAAC;IACpD,MAAM,CAAa;IACnB,MAAM,CAAS;IACf,OAAO,CAA6B;IACpC,OAAO,GAAG,KAAK,CAAC;IAExB,YACE,MAAkB,EAClB,MAAc,EACd,OAGC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QACtD,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,CAAC;IAClC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,4BAA4B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CACjE,CAAC;QAEF,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC5B,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpB,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACxC,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;YACrE,OAAO;QACT,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACnC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;gBACvC,IAAI,EAAE,WAAW;gBACjB,MAAM,EACJ,wEAAwE;oBACxE,+DAA+D;oBAC/D,wDAAwD;gBAC1D,WAAW,EAAE,IAAI;aAClB,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YACpC,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;gBAC7D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;gBAC5C,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YACjE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;QAChD,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import OpenAI from "openai";
|
|
3
|
+
import type { ToolDefinition, ToolCall, ToolExecutor } from "./modules/types.js";
|
|
4
|
+
export type LLMProvider = "claude_api" | "claude_subscription" | "openai_api" | "openai_subscription" | "openrouter";
|
|
5
|
+
export interface LLMClientOptions {
|
|
6
|
+
provider: LLMProvider;
|
|
7
|
+
model: string;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
oauthToken?: string;
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface LLMResponse {
|
|
13
|
+
text: string;
|
|
14
|
+
tool_calls: ToolCall[];
|
|
15
|
+
}
|
|
16
|
+
export declare class LLMClient {
|
|
17
|
+
private provider;
|
|
18
|
+
private model;
|
|
19
|
+
private mode;
|
|
20
|
+
private anthropic?;
|
|
21
|
+
private openai?;
|
|
22
|
+
constructor(options: LLMClientOptions);
|
|
23
|
+
chat(systemPrompt: string, messages: Anthropic.MessageParam[] | OpenAI.ChatCompletionMessageParam[], tools?: ToolDefinition[]): Promise<LLMResponse>;
|
|
24
|
+
agentLoop(systemPrompt: string, userPrompt: string, tools: ToolDefinition[], toolExecutor: ToolExecutor): Promise<string>;
|
|
25
|
+
private chatAnthropic;
|
|
26
|
+
private agentLoopAnthropic;
|
|
27
|
+
private chatOpenAI;
|
|
28
|
+
private agentLoopOpenAI;
|
|
29
|
+
}
|
package/dist/src/llm.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import OpenAI from "openai";
|
|
3
|
+
export class LLMClient {
|
|
4
|
+
provider;
|
|
5
|
+
model;
|
|
6
|
+
mode;
|
|
7
|
+
anthropic;
|
|
8
|
+
openai;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.mode = options.provider;
|
|
11
|
+
this.provider = this.mode.startsWith("claude") ? "anthropic" : "openai";
|
|
12
|
+
this.model = options.model;
|
|
13
|
+
if (this.mode === "claude_api") {
|
|
14
|
+
if (!options.apiKey) {
|
|
15
|
+
throw new Error("LLMClient missing required credential: apiKey");
|
|
16
|
+
}
|
|
17
|
+
this.anthropic = new Anthropic({
|
|
18
|
+
apiKey: options.apiKey,
|
|
19
|
+
baseURL: options.baseUrl,
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (this.mode === "claude_subscription") {
|
|
24
|
+
if (!options.oauthToken) {
|
|
25
|
+
throw new Error("LLMClient missing required credential: oauthToken");
|
|
26
|
+
}
|
|
27
|
+
this.anthropic = new Anthropic({
|
|
28
|
+
authToken: options.oauthToken,
|
|
29
|
+
baseURL: options.baseUrl,
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (this.mode === "openai_api") {
|
|
34
|
+
if (!options.apiKey) {
|
|
35
|
+
throw new Error("LLMClient missing required credential: apiKey");
|
|
36
|
+
}
|
|
37
|
+
this.openai = new OpenAI({
|
|
38
|
+
apiKey: options.apiKey,
|
|
39
|
+
baseURL: options.baseUrl,
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (this.mode === "openai_subscription") {
|
|
44
|
+
if (!options.oauthToken) {
|
|
45
|
+
throw new Error("LLMClient missing required credential: oauthToken");
|
|
46
|
+
}
|
|
47
|
+
this.openai = new OpenAI({
|
|
48
|
+
// OpenAI SDK uses bearer auth via apiKey; OAuth access tokens also work here.
|
|
49
|
+
apiKey: options.oauthToken,
|
|
50
|
+
baseURL: options.baseUrl,
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// openrouter
|
|
55
|
+
if (!options.apiKey) {
|
|
56
|
+
throw new Error("LLMClient missing required credential: apiKey");
|
|
57
|
+
}
|
|
58
|
+
this.openai = new OpenAI({
|
|
59
|
+
apiKey: options.apiKey,
|
|
60
|
+
baseURL: options.baseUrl ?? "https://openrouter.ai/api/v1",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async chat(systemPrompt, messages, tools) {
|
|
64
|
+
if (this.provider === "anthropic") {
|
|
65
|
+
return this.chatAnthropic(systemPrompt, messages, tools);
|
|
66
|
+
}
|
|
67
|
+
return this.chatOpenAI(systemPrompt, messages, tools);
|
|
68
|
+
}
|
|
69
|
+
async agentLoop(systemPrompt, userPrompt, tools, toolExecutor) {
|
|
70
|
+
if (this.provider === "anthropic") {
|
|
71
|
+
return this.agentLoopAnthropic(systemPrompt, userPrompt, tools, toolExecutor);
|
|
72
|
+
}
|
|
73
|
+
return this.agentLoopOpenAI(systemPrompt, userPrompt, tools, toolExecutor);
|
|
74
|
+
}
|
|
75
|
+
// --- Anthropic implementation ---
|
|
76
|
+
async chatAnthropic(systemPrompt, messages, tools) {
|
|
77
|
+
const response = await this.anthropic.messages.create({
|
|
78
|
+
model: this.model,
|
|
79
|
+
max_tokens: 4096,
|
|
80
|
+
system: systemPrompt,
|
|
81
|
+
messages,
|
|
82
|
+
...(tools?.length
|
|
83
|
+
? {
|
|
84
|
+
tools: tools.map((t) => ({
|
|
85
|
+
name: t.name,
|
|
86
|
+
description: t.description,
|
|
87
|
+
input_schema: t.input_schema,
|
|
88
|
+
})),
|
|
89
|
+
}
|
|
90
|
+
: {}),
|
|
91
|
+
});
|
|
92
|
+
let text = "";
|
|
93
|
+
const toolCalls = [];
|
|
94
|
+
for (const block of response.content) {
|
|
95
|
+
if (block.type === "text") {
|
|
96
|
+
text += block.text;
|
|
97
|
+
}
|
|
98
|
+
else if (block.type === "tool_use") {
|
|
99
|
+
toolCalls.push({
|
|
100
|
+
id: block.id,
|
|
101
|
+
name: block.name,
|
|
102
|
+
input: block.input,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { text, tool_calls: toolCalls };
|
|
107
|
+
}
|
|
108
|
+
async agentLoopAnthropic(systemPrompt, userPrompt, tools, toolExecutor) {
|
|
109
|
+
const MAX_ITERATIONS = 25;
|
|
110
|
+
const messages = [
|
|
111
|
+
{ role: "user", content: userPrompt },
|
|
112
|
+
];
|
|
113
|
+
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
|
114
|
+
const response = await this.chatAnthropic(systemPrompt, messages, tools);
|
|
115
|
+
if (response.tool_calls.length === 0) {
|
|
116
|
+
return response.text;
|
|
117
|
+
}
|
|
118
|
+
// Build assistant content blocks
|
|
119
|
+
const assistantContent = [];
|
|
120
|
+
if (response.text) {
|
|
121
|
+
assistantContent.push({ type: "text", text: response.text });
|
|
122
|
+
}
|
|
123
|
+
for (const tc of response.tool_calls) {
|
|
124
|
+
assistantContent.push({
|
|
125
|
+
type: "tool_use",
|
|
126
|
+
id: tc.id,
|
|
127
|
+
name: tc.name,
|
|
128
|
+
input: tc.input,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
132
|
+
// Execute tools and build results
|
|
133
|
+
const toolResults = [];
|
|
134
|
+
for (const tc of response.tool_calls) {
|
|
135
|
+
const result = await toolExecutor(tc);
|
|
136
|
+
toolResults.push({
|
|
137
|
+
type: "tool_result",
|
|
138
|
+
tool_use_id: tc.id,
|
|
139
|
+
content: result,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
messages.push({ role: "user", content: toolResults });
|
|
143
|
+
}
|
|
144
|
+
// If we hit the limit, return whatever text we have
|
|
145
|
+
const last = await this.chatAnthropic(systemPrompt, messages, []);
|
|
146
|
+
return last.text || "(Agent reached maximum tool call iterations)";
|
|
147
|
+
}
|
|
148
|
+
// --- OpenAI implementation ---
|
|
149
|
+
async chatOpenAI(systemPrompt, messages, tools) {
|
|
150
|
+
const response = await this.openai.chat.completions.create({
|
|
151
|
+
model: this.model,
|
|
152
|
+
messages: [
|
|
153
|
+
{ role: "system", content: systemPrompt },
|
|
154
|
+
...messages,
|
|
155
|
+
],
|
|
156
|
+
...(tools?.length
|
|
157
|
+
? {
|
|
158
|
+
tools: tools.map((t) => ({
|
|
159
|
+
type: "function",
|
|
160
|
+
function: {
|
|
161
|
+
name: t.name,
|
|
162
|
+
description: t.description,
|
|
163
|
+
parameters: t.input_schema,
|
|
164
|
+
},
|
|
165
|
+
})),
|
|
166
|
+
}
|
|
167
|
+
: {}),
|
|
168
|
+
});
|
|
169
|
+
const choice = response.choices[0];
|
|
170
|
+
const message = choice.message;
|
|
171
|
+
const text = message.content ?? "";
|
|
172
|
+
const toolCalls = [];
|
|
173
|
+
if (message.tool_calls) {
|
|
174
|
+
for (const tc of message.tool_calls) {
|
|
175
|
+
if (tc.type === "function") {
|
|
176
|
+
toolCalls.push({
|
|
177
|
+
id: tc.id,
|
|
178
|
+
name: tc.function.name,
|
|
179
|
+
input: JSON.parse(tc.function.arguments),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return { text, tool_calls: toolCalls };
|
|
185
|
+
}
|
|
186
|
+
async agentLoopOpenAI(systemPrompt, userPrompt, tools, toolExecutor) {
|
|
187
|
+
const MAX_ITERATIONS = 25;
|
|
188
|
+
const messages = [
|
|
189
|
+
{ role: "user", content: userPrompt },
|
|
190
|
+
];
|
|
191
|
+
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
|
192
|
+
const response = await this.chatOpenAI(systemPrompt, messages, tools);
|
|
193
|
+
if (response.tool_calls.length === 0) {
|
|
194
|
+
return response.text;
|
|
195
|
+
}
|
|
196
|
+
// Build assistant message with tool calls
|
|
197
|
+
const assistantMessage = {
|
|
198
|
+
role: "assistant",
|
|
199
|
+
content: response.text || null,
|
|
200
|
+
tool_calls: response.tool_calls.map((tc) => ({
|
|
201
|
+
id: tc.id,
|
|
202
|
+
type: "function",
|
|
203
|
+
function: {
|
|
204
|
+
name: tc.name,
|
|
205
|
+
arguments: JSON.stringify(tc.input),
|
|
206
|
+
},
|
|
207
|
+
})),
|
|
208
|
+
};
|
|
209
|
+
messages.push(assistantMessage);
|
|
210
|
+
// Execute tools and append results
|
|
211
|
+
for (const tc of response.tool_calls) {
|
|
212
|
+
const result = await toolExecutor(tc);
|
|
213
|
+
messages.push({
|
|
214
|
+
role: "tool",
|
|
215
|
+
tool_call_id: tc.id,
|
|
216
|
+
content: result,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// If we hit the limit, return whatever text we have
|
|
221
|
+
const last = await this.chatOpenAI(systemPrompt, messages, []);
|
|
222
|
+
return last.text || "(Agent reached maximum tool call iterations)";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=llm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/llm.ts"],"names":[],"mappings":"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"}
|