@m6d/cortex-server 1.1.1 → 1.1.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/README.md +38 -38
- package/dist/src/factory.d.ts +13 -1
- package/dist/src/ws/index.d.ts +1 -1
- package/package.json +54 -54
- package/src/adapters/database.ts +21 -28
- package/src/adapters/minio.ts +69 -69
- package/src/adapters/mssql.ts +171 -195
- package/src/adapters/storage.ts +4 -4
- package/src/ai/fetch.ts +31 -31
- package/src/ai/helpers.ts +18 -22
- package/src/ai/index.ts +101 -113
- package/src/ai/interceptors/resolve-captured-files.ts +42 -49
- package/src/ai/prompt.ts +80 -83
- package/src/ai/tools/call-endpoint.tool.ts +75 -82
- package/src/ai/tools/capture-files.tool.ts +15 -17
- package/src/ai/tools/execute-code.tool.ts +73 -80
- package/src/ai/tools/query-graph.tool.ts +17 -17
- package/src/auth/middleware.ts +51 -51
- package/src/cli/extract-endpoints.ts +436 -474
- package/src/config.ts +124 -134
- package/src/db/migrate.ts +13 -13
- package/src/db/migrations/20260309012148_cloudy_maria_hill/snapshot.json +303 -303
- package/src/db/schema.ts +46 -58
- package/src/factory.ts +136 -139
- package/src/graph/generate-cypher.ts +97 -97
- package/src/graph/helpers.ts +37 -37
- package/src/graph/index.ts +20 -20
- package/src/graph/neo4j.ts +82 -89
- package/src/graph/resolver.ts +201 -211
- package/src/graph/seed.ts +101 -114
- package/src/graph/types.ts +88 -88
- package/src/graph/validate.ts +55 -57
- package/src/index.ts +5 -5
- package/src/routes/chat.ts +23 -23
- package/src/routes/files.ts +75 -80
- package/src/routes/threads.ts +52 -54
- package/src/routes/ws.ts +22 -22
- package/src/types.ts +30 -30
- package/src/ws/connections.ts +11 -11
- package/src/ws/events.ts +2 -2
- package/src/ws/index.ts +1 -5
- package/src/ws/notify.ts +4 -4
package/src/routes/chat.ts
CHANGED
|
@@ -7,32 +7,32 @@ import { requireAuth } from "../auth/middleware.ts";
|
|
|
7
7
|
import { stream } from "../ai/index.ts";
|
|
8
8
|
|
|
9
9
|
export function createChatRoutes() {
|
|
10
|
-
|
|
10
|
+
const app = new Hono<CortexAppEnv>();
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
app.post(
|
|
13
|
+
"/chat",
|
|
14
|
+
requireAuth,
|
|
15
|
+
zValidator(
|
|
16
|
+
"json",
|
|
17
|
+
z.object({
|
|
18
|
+
id: z.string(),
|
|
19
|
+
messages: z.array(z.unknown()),
|
|
20
|
+
}),
|
|
21
|
+
),
|
|
22
|
+
async function (c) {
|
|
23
|
+
const config = c.get("agentConfig");
|
|
24
|
+
const { id: userId, token } = c.get("user");
|
|
25
|
+
const { messages, id: threadId } = c.req.valid("json");
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
const thread = await config.db.threads.getById(userId, threadId);
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
if (!thread) {
|
|
30
|
+
throw new HTTPException(404, { message: "Not found" });
|
|
31
|
+
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
return stream(messages, thread, userId, token, config);
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
return app;
|
|
38
38
|
}
|
package/src/routes/files.ts
CHANGED
|
@@ -7,99 +7,94 @@ import type { CortexAppEnv } from "../types.ts";
|
|
|
7
7
|
import { requireAuth } from "../auth/middleware.ts";
|
|
8
8
|
|
|
9
9
|
export function createFileRoutes() {
|
|
10
|
-
|
|
10
|
+
const app = new Hono<CortexAppEnv>();
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
app.get("/files/:id", requireAuth, async function (c) {
|
|
13
|
+
const config = c.get("agentConfig");
|
|
14
|
+
const id = c.req.param("id");
|
|
15
|
+
const { id: userId } = c.get("user");
|
|
16
|
+
const file = await config.db.capturedFiles.getById(id, userId);
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (!file) {
|
|
19
|
+
throw new HTTPException(404, { message: "File not found" });
|
|
20
|
+
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
const fileStream = await config.storage.stream(`captured_files/${id}`);
|
|
23
|
+
return new Response(fileStream, {
|
|
24
|
+
headers: {
|
|
25
|
+
"Content-Type": "application/octet-stream",
|
|
26
|
+
"Content-Disposition": `attachment; filename="${file.name}"`,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
28
29
|
});
|
|
29
|
-
});
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
31
|
+
app.post(
|
|
32
|
+
"/files",
|
|
33
|
+
requireAuth,
|
|
34
|
+
zValidator(
|
|
35
|
+
"json",
|
|
36
|
+
z.object({
|
|
37
|
+
toolCallId: z.string(),
|
|
38
|
+
message: z.unknown().transform(async (message, ctx) => {
|
|
39
|
+
const result = await safeValidateUIMessages({
|
|
40
|
+
messages: [message],
|
|
41
|
+
});
|
|
42
|
+
if (!result.success) {
|
|
43
|
+
ctx.addIssue({
|
|
44
|
+
code: "custom",
|
|
45
|
+
path: ["message"],
|
|
46
|
+
message: "Invalid message format",
|
|
47
|
+
});
|
|
48
|
+
return z.NEVER;
|
|
49
|
+
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
return result.data[0]!;
|
|
52
|
+
}),
|
|
53
|
+
files: z.array(
|
|
54
|
+
z.object({
|
|
55
|
+
id: z.string(),
|
|
56
|
+
file: z.object({
|
|
57
|
+
name: z.string(),
|
|
58
|
+
bytes: z.string(),
|
|
59
|
+
}),
|
|
60
|
+
}),
|
|
61
|
+
),
|
|
59
62
|
}),
|
|
60
|
-
}),
|
|
61
63
|
),
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const { message, toolCallId, files } = c.req.valid("json");
|
|
67
|
-
const { id: userId } = c.get("user");
|
|
64
|
+
async function (c) {
|
|
65
|
+
const config = c.get("agentConfig");
|
|
66
|
+
const { message, toolCallId, files } = c.req.valid("json");
|
|
67
|
+
const { id: userId } = c.get("user");
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
function isTool(value: UIMessage["parts"][number]): value is ToolUIPart {
|
|
70
|
+
return value.type.startsWith("tool-");
|
|
71
|
+
}
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
.filter(isTool)
|
|
75
|
-
.find((x) => x.toolCallId === toolCallId);
|
|
73
|
+
const toolPart = message.parts.filter(isTool).find((x) => x.toolCallId === toolCallId);
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
if (!toolPart) {
|
|
76
|
+
throw new HTTPException(423, {
|
|
77
|
+
message: "Invalid tool call id",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
82
80
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
const result = await config.db.capturedFiles.create(
|
|
82
|
+
userId,
|
|
83
|
+
message.id,
|
|
84
|
+
toolPart,
|
|
85
|
+
files,
|
|
86
|
+
);
|
|
89
87
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
);
|
|
97
|
-
}),
|
|
98
|
-
);
|
|
88
|
+
// Store file bytes
|
|
89
|
+
await Promise.all(
|
|
90
|
+
result.map(async (r, i) => {
|
|
91
|
+
await config.storage.put(`captured_files/${r.uploadId}`, files[i]!.file.bytes);
|
|
92
|
+
}),
|
|
93
|
+
);
|
|
99
94
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
95
|
+
return c.json(result);
|
|
96
|
+
},
|
|
97
|
+
);
|
|
103
98
|
|
|
104
|
-
|
|
99
|
+
return app;
|
|
105
100
|
}
|
package/src/routes/threads.ts
CHANGED
|
@@ -6,64 +6,62 @@ import { requireAuth } from "../auth/middleware.ts";
|
|
|
6
6
|
import { generateTitle } from "../ai/index.ts";
|
|
7
7
|
|
|
8
8
|
export function createThreadRoutes() {
|
|
9
|
-
|
|
9
|
+
const app = new Hono<CortexAppEnv>();
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return c.json(threads);
|
|
20
|
-
});
|
|
11
|
+
app.get("/threads", requireAuth, async function (c) {
|
|
12
|
+
const config = c.get("agentConfig");
|
|
13
|
+
const agentId = c.get("agentId");
|
|
14
|
+
const threads = await config.db.threads
|
|
15
|
+
.list(c.get("user").id, agentId)
|
|
16
|
+
.then((x) => x.map((y) => ({ id: y.id, title: y.title, createdAt: y.createdAt })));
|
|
17
|
+
return c.json(threads);
|
|
18
|
+
});
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
20
|
+
app.post(
|
|
21
|
+
"/threads",
|
|
22
|
+
requireAuth,
|
|
23
|
+
zValidator("json", z.object({ prompt: z.string() })),
|
|
24
|
+
async function (c) {
|
|
25
|
+
const config = c.get("agentConfig");
|
|
26
|
+
const agentId = c.get("agentId");
|
|
27
|
+
const { prompt } = c.req.valid("json");
|
|
28
|
+
const { id, createdAt, title } = await config.db.threads.create(
|
|
29
|
+
c.get("user").id,
|
|
30
|
+
agentId,
|
|
31
|
+
);
|
|
32
|
+
generateTitle(id, prompt, c.get("user").id, config);
|
|
33
|
+
return c.json({ id, title, createdAt });
|
|
34
|
+
},
|
|
35
|
+
);
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
app.delete("/threads/:threadId", requireAuth, async function (c) {
|
|
38
|
+
const config = c.get("agentConfig");
|
|
39
|
+
const threadId = c.req.param("threadId");
|
|
40
|
+
await config.db.threads.delete(c.get("user").id, threadId);
|
|
41
|
+
return c.body(null, 204);
|
|
42
|
+
});
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
app.get("/threads/:threadId/messages", requireAuth, async function (c) {
|
|
45
|
+
const config = c.get("agentConfig");
|
|
46
|
+
const threadId = c.req.param("threadId");
|
|
47
|
+
const messages = await config.db.messages
|
|
48
|
+
.list(c.get("user").id, threadId)
|
|
49
|
+
.then((x) => x.map((y) => y.content));
|
|
50
|
+
return c.json(messages);
|
|
51
|
+
});
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
53
|
+
app.post(
|
|
54
|
+
"/threads/:threadId/session",
|
|
55
|
+
requireAuth,
|
|
56
|
+
zValidator("json", z.object({ session: z.record(z.unknown()) })),
|
|
57
|
+
async function (c) {
|
|
58
|
+
const config = c.get("agentConfig");
|
|
59
|
+
const threadId = c.req.param("threadId");
|
|
60
|
+
const { session } = c.req.valid("json");
|
|
61
|
+
await config.db.threads.updateSession(threadId, session);
|
|
62
|
+
return c.body(null, 204);
|
|
63
|
+
},
|
|
64
|
+
);
|
|
67
65
|
|
|
68
|
-
|
|
66
|
+
return app;
|
|
69
67
|
}
|
package/src/routes/ws.ts
CHANGED
|
@@ -4,30 +4,30 @@ import type { AppEnv } from "../types.ts";
|
|
|
4
4
|
import { addConnection, removeConnection } from "../ws/index.ts";
|
|
5
5
|
|
|
6
6
|
export function createWsRoute() {
|
|
7
|
-
|
|
7
|
+
const app = new Hono<AppEnv>();
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
app.get(
|
|
10
|
+
"/ws",
|
|
11
|
+
upgradeWebSocket(function (c) {
|
|
12
|
+
const user = c.get("user");
|
|
13
|
+
const userId = user?.id;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
return {
|
|
16
|
+
onOpen(_, ws) {
|
|
17
|
+
if (userId) {
|
|
18
|
+
addConnection(userId, ws);
|
|
19
|
+
}
|
|
20
|
+
ws.send("Connected!");
|
|
21
|
+
},
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
onClose(_, ws) {
|
|
24
|
+
if (userId) {
|
|
25
|
+
removeConnection(userId, ws);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
return app;
|
|
33
33
|
}
|
package/src/types.ts
CHANGED
|
@@ -2,49 +2,49 @@ import type { ToolUIPart, UIMessage } from "ai";
|
|
|
2
2
|
import type { ResolvedCortexAgentConfig } from "./config";
|
|
3
3
|
|
|
4
4
|
export type Thread = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
id: string;
|
|
6
|
+
userId: string;
|
|
7
|
+
agentId: string;
|
|
8
|
+
title: string | null;
|
|
9
|
+
session: Record<string, unknown> | null;
|
|
10
|
+
createdAt: Date;
|
|
11
|
+
updatedAt: Date;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
export type StoredMessage = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
id: string;
|
|
16
|
+
threadId: string;
|
|
17
|
+
text: string | null;
|
|
18
|
+
content: UIMessage;
|
|
19
|
+
role: "system" | "user" | "assistant" | "tool";
|
|
20
|
+
createdAt: Date;
|
|
21
|
+
updatedAt: Date;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export type CapturedFileInput = {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
id: string;
|
|
26
|
+
file: { name: string; bytes: string };
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
export type AppEnv = {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
Bindings: {};
|
|
31
|
+
Variables: {
|
|
32
|
+
user: { id: string; token: string } | undefined;
|
|
33
|
+
};
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
export type AuthedAppEnv = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
Bindings: {};
|
|
38
|
+
Variables: {
|
|
39
|
+
user: { id: string; token: string };
|
|
40
|
+
};
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
export type CortexAppEnv = {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
Bindings: {};
|
|
45
|
+
Variables: {
|
|
46
|
+
user: { id: string; token: string };
|
|
47
|
+
agentConfig: ResolvedCortexAgentConfig;
|
|
48
|
+
agentId: string;
|
|
49
|
+
};
|
|
50
50
|
};
|
package/src/ws/connections.ts
CHANGED
|
@@ -3,21 +3,21 @@ import type { WSContext } from "hono/ws";
|
|
|
3
3
|
const connections = new Map<string, WSContext[]>();
|
|
4
4
|
|
|
5
5
|
export function addConnection(userId: string, ws: WSContext) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const sockets = connections.get(userId) ?? [];
|
|
7
|
+
sockets.push(ws);
|
|
8
|
+
connections.set(userId, sockets);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function removeConnection(userId: string, ws: WSContext) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
let sockets = connections.get(userId) ?? [];
|
|
13
|
+
sockets = sockets.filter((x) => x !== ws);
|
|
14
|
+
if (sockets.length) {
|
|
15
|
+
connections.set(userId, sockets);
|
|
16
|
+
} else {
|
|
17
|
+
connections.delete(userId);
|
|
18
|
+
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function getConnections(userId: string) {
|
|
22
|
-
|
|
22
|
+
return connections.get(userId) ?? [];
|
|
23
23
|
}
|
package/src/ws/events.ts
CHANGED
package/src/ws/index.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
addConnection,
|
|
3
|
-
removeConnection,
|
|
4
|
-
getConnections,
|
|
5
|
-
} from "./connections.ts";
|
|
1
|
+
export { addConnection, removeConnection, getConnections } from "./connections.ts";
|
|
6
2
|
export { notify } from "./notify.ts";
|
|
7
3
|
export type { WsEvent, ThreadTitleUpdatedEvent } from "./events.ts";
|
package/src/ws/notify.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { getConnections } from "./connections.ts";
|
|
|
2
2
|
import type { WsEvent } from "./events.ts";
|
|
3
3
|
|
|
4
4
|
export function notify(userId: string, event: WsEvent) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const message = JSON.stringify(event);
|
|
6
|
+
for (const ws of getConnections(userId)) {
|
|
7
|
+
ws.send(message);
|
|
8
|
+
}
|
|
9
9
|
}
|