goto-assistant 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 +21 -0
- package/README.md +113 -0
- package/bin/goto-assistant.js +9 -0
- package/dist/agents/claude.d.ts +7 -0
- package/dist/agents/claude.js +75 -0
- package/dist/agents/claude.js.map +1 -0
- package/dist/agents/openai.d.ts +3 -0
- package/dist/agents/openai.js +109 -0
- package/dist/agents/openai.js.map +1 -0
- package/dist/agents/router.d.ts +15 -0
- package/dist/agents/router.js +17 -0
- package/dist/agents/router.js.map +1 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.js +67 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +253 -0
- package/dist/server.js.map +1 -0
- package/dist/sessions.d.ts +35 -0
- package/dist/sessions.js +98 -0
- package/dist/sessions.js.map +1 -0
- package/dist/uploads.d.ts +15 -0
- package/dist/uploads.js +41 -0
- package/dist/uploads.js.map +1 -0
- package/package.json +65 -0
- package/public/cron-sync.js +50 -0
- package/public/index.html +320 -0
- package/public/setup.html +296 -0
- package/public/style.css +200 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
3
|
+
import http from "node:http";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import multer from "multer";
|
|
6
|
+
import { isConfigured, loadConfig, saveConfig, getMaskedConfig, loadMcpServers, saveMcpServers, getMaskedMcpServers } from "./config.js";
|
|
7
|
+
import { createConversation, getConversation, updateSessionId, updateTitle, listConversations, saveMessage, getMessages, deleteConversation } from "./sessions.js";
|
|
8
|
+
import { routeMessage } from "./agents/router.js";
|
|
9
|
+
import { saveUpload, getUpload, ALLOWED_IMAGE_TYPES, UPLOADS_DIR } from "./uploads.js";
|
|
10
|
+
export function createApp() {
|
|
11
|
+
const app = express();
|
|
12
|
+
app.use(express.json());
|
|
13
|
+
app.use(express.urlencoded({ extended: true }));
|
|
14
|
+
// Setup redirect middleware: if not configured, redirect to setup page
|
|
15
|
+
app.use((req, res, next) => {
|
|
16
|
+
if (!isConfigured() &&
|
|
17
|
+
!req.path.startsWith("/setup") &&
|
|
18
|
+
!req.path.startsWith("/api/") &&
|
|
19
|
+
!req.path.endsWith(".css") &&
|
|
20
|
+
!req.path.endsWith(".js") &&
|
|
21
|
+
req.path !== "/health") {
|
|
22
|
+
res.redirect("/setup.html");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
next();
|
|
26
|
+
});
|
|
27
|
+
// Static files
|
|
28
|
+
app.use(express.static(path.join(import.meta.dirname, "..", "public")));
|
|
29
|
+
// Health check
|
|
30
|
+
app.get("/health", (_req, res) => {
|
|
31
|
+
res.json({ status: "ok", configured: isConfigured() });
|
|
32
|
+
});
|
|
33
|
+
// List models for setup page
|
|
34
|
+
app.post("/api/models", async (req, res) => {
|
|
35
|
+
const { provider, apiKey, baseUrl } = req.body;
|
|
36
|
+
if (provider === "claude") {
|
|
37
|
+
// Anthropic doesn't have a list models endpoint; return known models
|
|
38
|
+
res.json({
|
|
39
|
+
models: [
|
|
40
|
+
{ id: "claude-sonnet-4-5-20250929", name: "Claude Sonnet 4.5" },
|
|
41
|
+
{ id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5" },
|
|
42
|
+
{ id: "claude-opus-4-6", name: "Claude Opus 4.6" },
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (provider === "openai") {
|
|
48
|
+
try {
|
|
49
|
+
const url = `${baseUrl || "https://api.openai.com"}/v1/models`;
|
|
50
|
+
const response = await fetch(url, {
|
|
51
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
res.status(400).json({ error: "Failed to fetch models. Check your API key." });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const data = (await response.json());
|
|
58
|
+
const models = data.data
|
|
59
|
+
.map((m) => ({ id: m.id, name: m.id }))
|
|
60
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
61
|
+
res.json({ models });
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
res.status(500).json({ error: "Failed to connect to OpenAI API" });
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
res.status(400).json({ error: "Invalid provider" });
|
|
69
|
+
});
|
|
70
|
+
// Save config from setup page
|
|
71
|
+
app.post("/api/setup", (req, res) => {
|
|
72
|
+
const { mcpServers, ...rest } = req.body;
|
|
73
|
+
const config = rest;
|
|
74
|
+
if (!config.provider || !config.server?.port) {
|
|
75
|
+
res.status(400).json({ error: "Invalid config" });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
saveConfig(config);
|
|
79
|
+
if (mcpServers) {
|
|
80
|
+
saveMcpServers(mcpServers);
|
|
81
|
+
}
|
|
82
|
+
res.json({ ok: true });
|
|
83
|
+
});
|
|
84
|
+
// Get masked config for settings page
|
|
85
|
+
app.get("/api/config", (_req, res) => {
|
|
86
|
+
if (!isConfigured()) {
|
|
87
|
+
res.json({ configured: false });
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const config = loadConfig();
|
|
91
|
+
res.json({ configured: true, config: getMaskedConfig(config) });
|
|
92
|
+
});
|
|
93
|
+
// MCP servers endpoints
|
|
94
|
+
app.get("/api/mcp-servers", (_req, res) => {
|
|
95
|
+
const servers = loadMcpServers();
|
|
96
|
+
res.json({ mcpServers: getMaskedMcpServers(servers) });
|
|
97
|
+
});
|
|
98
|
+
app.post("/api/mcp-servers", (req, res) => {
|
|
99
|
+
const { mcpServers } = req.body;
|
|
100
|
+
if (!mcpServers || typeof mcpServers !== "object") {
|
|
101
|
+
res.status(400).json({ error: "Invalid mcpServers" });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
saveMcpServers(mcpServers);
|
|
105
|
+
res.json({ ok: true });
|
|
106
|
+
});
|
|
107
|
+
// List conversations
|
|
108
|
+
app.get("/api/conversations", (_req, res) => {
|
|
109
|
+
const conversations = listConversations();
|
|
110
|
+
res.json({ conversations });
|
|
111
|
+
});
|
|
112
|
+
// Get messages for a conversation
|
|
113
|
+
app.get("/api/conversations/:id/messages", (req, res) => {
|
|
114
|
+
const messages = getMessages(req.params.id);
|
|
115
|
+
res.json({ messages });
|
|
116
|
+
});
|
|
117
|
+
// Delete a conversation
|
|
118
|
+
app.delete("/api/conversations/:id", (req, res) => {
|
|
119
|
+
deleteConversation(req.params.id);
|
|
120
|
+
res.json({ ok: true });
|
|
121
|
+
});
|
|
122
|
+
// File upload
|
|
123
|
+
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 20 * 1024 * 1024 } });
|
|
124
|
+
app.post("/api/upload", upload.single("file"), (req, res) => {
|
|
125
|
+
const file = req.file;
|
|
126
|
+
if (!file) {
|
|
127
|
+
res.status(400).json({ error: "No file provided" });
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (!ALLOWED_IMAGE_TYPES.includes(file.mimetype)) {
|
|
131
|
+
res.status(400).json({ error: `Unsupported file type: ${file.mimetype}. Allowed: ${ALLOWED_IMAGE_TYPES.join(", ")}` });
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const meta = saveUpload(file.buffer, file.originalname, file.mimetype);
|
|
135
|
+
res.json({ fileId: meta.fileId, filename: meta.filename, mimeType: meta.mimeType, size: meta.size });
|
|
136
|
+
});
|
|
137
|
+
// Serve uploaded files
|
|
138
|
+
app.get("/api/uploads/:fileId", (req, res) => {
|
|
139
|
+
const result = getUpload(req.params.fileId);
|
|
140
|
+
if (!result) {
|
|
141
|
+
res.status(404).json({ error: "File not found" });
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
res.setHeader("Content-Type", result.mimeType);
|
|
145
|
+
res.setHeader("Content-Disposition", `inline; filename="${result.filename}"`);
|
|
146
|
+
res.send(result.data);
|
|
147
|
+
});
|
|
148
|
+
return app;
|
|
149
|
+
}
|
|
150
|
+
export function createServer(app) {
|
|
151
|
+
const server = http.createServer(app);
|
|
152
|
+
const wss = new WebSocketServer({ server });
|
|
153
|
+
wss.on("connection", (ws) => {
|
|
154
|
+
ws.on("message", async (raw) => {
|
|
155
|
+
let msg;
|
|
156
|
+
try {
|
|
157
|
+
msg = JSON.parse(raw.toString());
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
ws.send(JSON.stringify({ type: "error", text: "Invalid JSON" }));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (msg.type !== "message" || !msg.text) {
|
|
164
|
+
ws.send(JSON.stringify({ type: "error", text: "Invalid message format" }));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (!isConfigured()) {
|
|
168
|
+
ws.send(JSON.stringify({ type: "error", text: "Not configured. Visit /setup.html" }));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const config = loadConfig();
|
|
172
|
+
const mcpServers = loadMcpServers();
|
|
173
|
+
// Get or create conversation
|
|
174
|
+
let conversationId = msg.conversationId;
|
|
175
|
+
let resumeSessionId;
|
|
176
|
+
let isNewConversation = false;
|
|
177
|
+
if (conversationId) {
|
|
178
|
+
const existing = getConversation(conversationId);
|
|
179
|
+
if (existing?.sdk_session_id) {
|
|
180
|
+
resumeSessionId = existing.sdk_session_id;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!conversationId) {
|
|
184
|
+
const conv = createConversation(config.provider);
|
|
185
|
+
conversationId = conv.id;
|
|
186
|
+
isNewConversation = true;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
// Save user message (with attachment metadata if present)
|
|
190
|
+
const msgAttachments = msg.attachments;
|
|
191
|
+
if (msgAttachments && msgAttachments.length > 0) {
|
|
192
|
+
saveMessage(conversationId, "user", JSON.stringify({
|
|
193
|
+
text: msg.text,
|
|
194
|
+
attachments: msgAttachments.map(a => ({ fileId: a.fileId, filename: a.filename, mimeType: a.mimeType })),
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
saveMessage(conversationId, "user", msg.text);
|
|
199
|
+
}
|
|
200
|
+
// Set title from first message on new conversations
|
|
201
|
+
if (isNewConversation) {
|
|
202
|
+
const title = msg.text.length > 100 ? msg.text.slice(0, 100) + "..." : msg.text;
|
|
203
|
+
updateTitle(conversationId, title);
|
|
204
|
+
}
|
|
205
|
+
// Resolve attachment data from disk
|
|
206
|
+
let attachments;
|
|
207
|
+
if (msgAttachments && msgAttachments.length > 0) {
|
|
208
|
+
attachments = [];
|
|
209
|
+
for (const att of msgAttachments) {
|
|
210
|
+
const upload = getUpload(att.fileId);
|
|
211
|
+
if (upload) {
|
|
212
|
+
attachments.push({
|
|
213
|
+
filename: upload.filename,
|
|
214
|
+
mimeType: upload.mimeType,
|
|
215
|
+
data: upload.data,
|
|
216
|
+
filePath: path.resolve(UPLOADS_DIR, att.fileId, upload.filename),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Load conversation history (excluding current message) for providers that need it
|
|
222
|
+
const allMessages = getMessages(conversationId);
|
|
223
|
+
const history = allMessages.slice(0, -1); // exclude the message we just saved
|
|
224
|
+
let responseText = "";
|
|
225
|
+
const result = await routeMessage(msg.text, config, mcpServers, (chunk) => {
|
|
226
|
+
responseText += chunk;
|
|
227
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
228
|
+
ws.send(JSON.stringify({ type: "chunk", text: chunk }));
|
|
229
|
+
}
|
|
230
|
+
}, resumeSessionId, attachments, history);
|
|
231
|
+
// Save assistant message
|
|
232
|
+
if (responseText) {
|
|
233
|
+
saveMessage(conversationId, "assistant", responseText);
|
|
234
|
+
}
|
|
235
|
+
// Save SDK session ID for resume
|
|
236
|
+
if (result.sessionId) {
|
|
237
|
+
updateSessionId(conversationId, result.sessionId);
|
|
238
|
+
}
|
|
239
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
240
|
+
ws.send(JSON.stringify({ type: "done", conversationId }));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
const errorText = err instanceof Error ? err.message : "Unknown error";
|
|
245
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
246
|
+
ws.send(JSON.stringify({ type: "error", text: errorText }));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
return server;
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
export interface Conversation {
|
|
3
|
+
id: string;
|
|
4
|
+
provider: string;
|
|
5
|
+
sdk_session_id: string | null;
|
|
6
|
+
title: string | null;
|
|
7
|
+
created_at: string;
|
|
8
|
+
updated_at: string;
|
|
9
|
+
}
|
|
10
|
+
export interface Message {
|
|
11
|
+
id: number;
|
|
12
|
+
conversation_id: string;
|
|
13
|
+
role: string;
|
|
14
|
+
content: string;
|
|
15
|
+
created_at: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function getDb(): Database.Database;
|
|
18
|
+
export declare function createConversation(provider: string): Conversation;
|
|
19
|
+
export declare function getConversation(id: string): Conversation | undefined;
|
|
20
|
+
export declare function updateSessionId(conversationId: string, sdkSessionId: string): void;
|
|
21
|
+
export declare function updateTitle(conversationId: string, title: string): void;
|
|
22
|
+
export declare function listConversations(): Conversation[];
|
|
23
|
+
export declare function saveMessage(conversationId: string, role: string, content: string): void;
|
|
24
|
+
export declare function getMessages(conversationId: string): Message[];
|
|
25
|
+
export declare function deleteConversation(id: string): void;
|
|
26
|
+
export interface ParsedContent {
|
|
27
|
+
text: string;
|
|
28
|
+
attachments?: Array<{
|
|
29
|
+
fileId: string;
|
|
30
|
+
filename: string;
|
|
31
|
+
mimeType: string;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
export declare function parseMessageContent(content: string): ParsedContent;
|
|
35
|
+
export declare function closeDb(): void;
|
package/dist/sessions.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
import { DATA_DIR } from "./config.js";
|
|
6
|
+
const DB_PATH = path.join(DATA_DIR, "sessions.db");
|
|
7
|
+
let db = null;
|
|
8
|
+
export function getDb() {
|
|
9
|
+
if (db)
|
|
10
|
+
return db;
|
|
11
|
+
if (!fs.existsSync(DATA_DIR)) {
|
|
12
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
db = new Database(DB_PATH);
|
|
15
|
+
db.pragma("journal_mode = WAL");
|
|
16
|
+
db.exec(`
|
|
17
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
18
|
+
id TEXT PRIMARY KEY,
|
|
19
|
+
provider TEXT NOT NULL,
|
|
20
|
+
sdk_session_id TEXT,
|
|
21
|
+
title TEXT,
|
|
22
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
23
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
24
|
+
)
|
|
25
|
+
`);
|
|
26
|
+
db.exec(`
|
|
27
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
28
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29
|
+
conversation_id TEXT NOT NULL,
|
|
30
|
+
role TEXT NOT NULL,
|
|
31
|
+
content TEXT NOT NULL,
|
|
32
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
33
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id)
|
|
34
|
+
)
|
|
35
|
+
`);
|
|
36
|
+
return db;
|
|
37
|
+
}
|
|
38
|
+
export function createConversation(provider) {
|
|
39
|
+
const id = crypto.randomUUID();
|
|
40
|
+
const now = new Date().toISOString();
|
|
41
|
+
getDb()
|
|
42
|
+
.prepare("INSERT INTO conversations (id, provider, created_at, updated_at) VALUES (?, ?, ?, ?)")
|
|
43
|
+
.run(id, provider, now, now);
|
|
44
|
+
return { id, provider, sdk_session_id: null, title: null, created_at: now, updated_at: now };
|
|
45
|
+
}
|
|
46
|
+
export function getConversation(id) {
|
|
47
|
+
return getDb()
|
|
48
|
+
.prepare("SELECT * FROM conversations WHERE id = ?")
|
|
49
|
+
.get(id);
|
|
50
|
+
}
|
|
51
|
+
export function updateSessionId(conversationId, sdkSessionId) {
|
|
52
|
+
getDb()
|
|
53
|
+
.prepare("UPDATE conversations SET sdk_session_id = ?, updated_at = datetime('now') WHERE id = ?")
|
|
54
|
+
.run(sdkSessionId, conversationId);
|
|
55
|
+
}
|
|
56
|
+
export function updateTitle(conversationId, title) {
|
|
57
|
+
getDb()
|
|
58
|
+
.prepare("UPDATE conversations SET title = ?, updated_at = datetime('now') WHERE id = ?")
|
|
59
|
+
.run(title, conversationId);
|
|
60
|
+
}
|
|
61
|
+
export function listConversations() {
|
|
62
|
+
return getDb()
|
|
63
|
+
.prepare("SELECT * FROM conversations ORDER BY updated_at DESC")
|
|
64
|
+
.all();
|
|
65
|
+
}
|
|
66
|
+
export function saveMessage(conversationId, role, content) {
|
|
67
|
+
getDb()
|
|
68
|
+
.prepare("INSERT INTO messages (conversation_id, role, content) VALUES (?, ?, ?)")
|
|
69
|
+
.run(conversationId, role, content);
|
|
70
|
+
}
|
|
71
|
+
export function getMessages(conversationId) {
|
|
72
|
+
return getDb()
|
|
73
|
+
.prepare("SELECT * FROM messages WHERE conversation_id = ? ORDER BY id ASC")
|
|
74
|
+
.all(conversationId);
|
|
75
|
+
}
|
|
76
|
+
export function deleteConversation(id) {
|
|
77
|
+
getDb().prepare("DELETE FROM messages WHERE conversation_id = ?").run(id);
|
|
78
|
+
getDb().prepare("DELETE FROM conversations WHERE id = ?").run(id);
|
|
79
|
+
}
|
|
80
|
+
export function parseMessageContent(content) {
|
|
81
|
+
try {
|
|
82
|
+
const parsed = JSON.parse(content);
|
|
83
|
+
if (parsed && typeof parsed.text === "string") {
|
|
84
|
+
return { text: parsed.text, attachments: parsed.attachments };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Not JSON — treat as plain text
|
|
89
|
+
}
|
|
90
|
+
return { text: content };
|
|
91
|
+
}
|
|
92
|
+
export function closeDb() {
|
|
93
|
+
if (db) {
|
|
94
|
+
db.close();
|
|
95
|
+
db = null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const UPLOADS_DIR: string;
|
|
2
|
+
export declare const ALLOWED_IMAGE_TYPES: string[];
|
|
3
|
+
export interface UploadMeta {
|
|
4
|
+
fileId: string;
|
|
5
|
+
filename: string;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
size: number;
|
|
8
|
+
path: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function saveUpload(buffer: Buffer, filename: string, mimeType: string): UploadMeta;
|
|
11
|
+
export declare function getUpload(fileId: string): {
|
|
12
|
+
data: Buffer;
|
|
13
|
+
filename: string;
|
|
14
|
+
mimeType: string;
|
|
15
|
+
} | null;
|
package/dist/uploads.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import { DATA_DIR } from "./config.js";
|
|
5
|
+
export const UPLOADS_DIR = path.join(DATA_DIR, "uploads");
|
|
6
|
+
export const ALLOWED_IMAGE_TYPES = [
|
|
7
|
+
"image/jpeg",
|
|
8
|
+
"image/png",
|
|
9
|
+
"image/gif",
|
|
10
|
+
"image/webp",
|
|
11
|
+
];
|
|
12
|
+
export function saveUpload(buffer, filename, mimeType) {
|
|
13
|
+
const fileId = crypto.randomUUID();
|
|
14
|
+
const dir = path.join(UPLOADS_DIR, fileId);
|
|
15
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
+
const filePath = path.join(dir, filename);
|
|
17
|
+
fs.writeFileSync(filePath, buffer);
|
|
18
|
+
return { fileId, filename, mimeType, size: buffer.length, path: filePath };
|
|
19
|
+
}
|
|
20
|
+
export function getUpload(fileId) {
|
|
21
|
+
const dir = path.join(UPLOADS_DIR, fileId);
|
|
22
|
+
if (!fs.existsSync(dir))
|
|
23
|
+
return null;
|
|
24
|
+
const files = fs.readdirSync(dir);
|
|
25
|
+
if (files.length === 0)
|
|
26
|
+
return null;
|
|
27
|
+
const filename = files[0];
|
|
28
|
+
const filePath = path.join(dir, filename);
|
|
29
|
+
const data = fs.readFileSync(filePath);
|
|
30
|
+
const ext = path.extname(filename).toLowerCase();
|
|
31
|
+
const mimeMap = {
|
|
32
|
+
".jpg": "image/jpeg",
|
|
33
|
+
".jpeg": "image/jpeg",
|
|
34
|
+
".png": "image/png",
|
|
35
|
+
".gif": "image/gif",
|
|
36
|
+
".webp": "image/webp",
|
|
37
|
+
};
|
|
38
|
+
const mimeType = mimeMap[ext] || "application/octet-stream";
|
|
39
|
+
return { data, filename, mimeType };
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=uploads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploads.js","sourceRoot":"","sources":["../src/uploads.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAE1D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,YAAY;IACZ,WAAW;IACX,WAAW;IACX,YAAY;CACb,CAAC;AAUF,MAAM,UAAU,UAAU,CACxB,MAAc,EACd,QAAgB,EAChB,QAAgB;IAEhB,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC3C,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC1C,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,MAAc;IAEd,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,MAAM,OAAO,GAA2B;QACtC,MAAM,EAAE,YAAY;QACpB,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,WAAW;QACnB,MAAM,EAAE,WAAW;QACnB,OAAO,EAAE,YAAY;KACtB,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;IAC5D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACtC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "goto-assistant",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lightweight, self-hosted personal AI assistant",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"packageManager": "pnpm@10.29.3",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"goto-assistant": "./bin/goto-assistant.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/",
|
|
13
|
+
"public/",
|
|
14
|
+
"bin/"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20.11.0"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/jolks/goto-assistant"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"ai",
|
|
25
|
+
"assistant",
|
|
26
|
+
"chat",
|
|
27
|
+
"claude",
|
|
28
|
+
"openai",
|
|
29
|
+
"mcp",
|
|
30
|
+
"self-hosted"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"dev": "tsx src/index.ts",
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"start": "node dist/index.js",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest",
|
|
38
|
+
"prepublishOnly": "npm run build"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.0",
|
|
42
|
+
"@openai/agents": "^0.4.0",
|
|
43
|
+
"@openai/agents-extensions": "^0.4.0",
|
|
44
|
+
"better-sqlite3": "^11.0.0",
|
|
45
|
+
"express": "^5.0.0",
|
|
46
|
+
"multer": "^2.0.2",
|
|
47
|
+
"ws": "^8.0.0",
|
|
48
|
+
"zod": "^4.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/better-sqlite3": "^7.0.0",
|
|
52
|
+
"@types/express": "^5.0.0",
|
|
53
|
+
"@types/multer": "^2.0.0",
|
|
54
|
+
"@types/ws": "^8.0.0",
|
|
55
|
+
"tsx": "^4.0.0",
|
|
56
|
+
"typescript": "^5.0.0",
|
|
57
|
+
"vitest": "^3.0.0"
|
|
58
|
+
},
|
|
59
|
+
"pnpm": {
|
|
60
|
+
"onlyBuiltDependencies": [
|
|
61
|
+
"better-sqlite3",
|
|
62
|
+
"esbuild"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// buildCronConfig — derive mcp-cron args and env from the user's AI settings.
|
|
2
|
+
//
|
|
3
|
+
// Core rule: if baseUrl is set (LiteLLM proxy), cron always uses
|
|
4
|
+
// --ai-provider openai + MCP_CRON_AI_API_KEY, because mcp-cron's Anthropic
|
|
5
|
+
// provider does not support --ai-base-url.
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line no-unused-vars
|
|
8
|
+
function buildCronConfig({ provider, apiKey, model, baseUrl, currentArgs }) {
|
|
9
|
+
const useProxy = Boolean(baseUrl);
|
|
10
|
+
const aiProvider = useProxy ? 'openai' : (provider === 'claude' ? 'anthropic' : 'openai');
|
|
11
|
+
|
|
12
|
+
// Update --ai-provider
|
|
13
|
+
let args = currentArgs.replace(/--ai-provider \S+/, `--ai-provider ${aiProvider}`);
|
|
14
|
+
|
|
15
|
+
// Update --ai-model
|
|
16
|
+
if (model) {
|
|
17
|
+
if (/--ai-model \S+/.test(args)) {
|
|
18
|
+
args = args.replace(/--ai-model \S+/, `--ai-model ${model}`);
|
|
19
|
+
} else {
|
|
20
|
+
args += ` --ai-model ${model}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Update --ai-base-url
|
|
25
|
+
if (baseUrl) {
|
|
26
|
+
if (/--ai-base-url \S+/.test(args)) {
|
|
27
|
+
args = args.replace(/--ai-base-url \S+/, `--ai-base-url ${baseUrl}`);
|
|
28
|
+
} else {
|
|
29
|
+
args += ` --ai-base-url ${baseUrl}`;
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
args = args.replace(/\s*--ai-base-url \S+/, '');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Determine env key
|
|
36
|
+
let envKey;
|
|
37
|
+
if (useProxy) {
|
|
38
|
+
envKey = 'MCP_CRON_AI_API_KEY';
|
|
39
|
+
} else if (provider === 'claude') {
|
|
40
|
+
envKey = 'ANTHROPIC_API_KEY';
|
|
41
|
+
} else {
|
|
42
|
+
envKey = 'OPENAI_API_KEY';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { args, envKey, envValue: apiKey };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
49
|
+
module.exports = { buildCronConfig };
|
|
50
|
+
}
|