@bharatpanigrahi/onw-cli 1.0.1
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/package.json +35 -0
- package/src/cli/ai/google-service.js +102 -0
- package/src/cli/chat/chat-with-ai-agent.js +209 -0
- package/src/cli/chat/chat-with-ai-tool.js +325 -0
- package/src/cli/chat/chat-with-ai.js +242 -0
- package/src/cli/commands/ai/wakeUp.js +83 -0
- package/src/cli/commands/auth/login.js +287 -0
- package/src/cli/main.js +45 -0
- package/src/config/agent.config.js +163 -0
- package/src/config/google.config.js +7 -0
- package/src/config/tool.config.js +105 -0
- package/src/index.js +36 -0
- package/src/lib/auth.js +34 -0
- package/src/lib/db.js +8 -0
- package/src/lib/token.js +85 -0
- package/src/service/chat.service.js +104 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { generateObject } from "ai";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
const ApplicationSchema = z.object({
|
|
8
|
+
folderName: z.string().describe("Kebab-Case folder name for the application"),
|
|
9
|
+
description: z.string().describe("Brief description of what was created"),
|
|
10
|
+
files: z.array(
|
|
11
|
+
z
|
|
12
|
+
.object({
|
|
13
|
+
path: z.string().describe("Relative file path (e.g src.App.jsx)"),
|
|
14
|
+
content: z.string().describe("Complete File content"),
|
|
15
|
+
})
|
|
16
|
+
.describe("All files needed for the application"),
|
|
17
|
+
),
|
|
18
|
+
setupCommands: z.array(
|
|
19
|
+
z
|
|
20
|
+
.string()
|
|
21
|
+
.describe(
|
|
22
|
+
"Bash commands to setup and run (e.g: npm install, npm run dev)",
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
dependecies: z.record(z.string(), z.string()).default({}),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function printSystem(message) {
|
|
29
|
+
console.log(message);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function displayFileTree(files, folderName) {
|
|
33
|
+
printSystem(chalk.cyan("\nš Project Structure: "));
|
|
34
|
+
printSystem(chalk.white(`${folderName}/`));
|
|
35
|
+
|
|
36
|
+
const filesByDir = {};
|
|
37
|
+
files.forEach((file) => {
|
|
38
|
+
const parts = file.path.split("/");
|
|
39
|
+
const dir = parts.length > 1 ? parts.slice(0, -1).join("/") : "";
|
|
40
|
+
|
|
41
|
+
if (!filesByDir[dir]) {
|
|
42
|
+
filesByDir[dir] = [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
filesByDir[dir].push(parts[parts.length - 1]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
Object.keys(filesByDir)
|
|
49
|
+
.sort()
|
|
50
|
+
.forEach((dir) => {
|
|
51
|
+
if (dir) {
|
|
52
|
+
printSystem(chalk.white(`āāā${dir}/`));
|
|
53
|
+
filesByDir[dir].forEach((file) => {
|
|
54
|
+
printSystem(chalk.white(`ā āāā${file}`));
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
filesByDir[dir].forEach((file) => {
|
|
58
|
+
printSystem(chalk.white(`āāā${file}`));
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function createApplicationFiles(baseDir, folderName, files) {
|
|
65
|
+
const appDir = path.join(baseDir, folderName);
|
|
66
|
+
await fs.mkdir(appDir, { recursive: true });
|
|
67
|
+
|
|
68
|
+
printSystem(chalk.cyan("\nš Created directory: ${folderName}"));
|
|
69
|
+
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
const filePath = path.join(appDir, file.path);
|
|
72
|
+
const fileDir = path.dirname(filePath);
|
|
73
|
+
|
|
74
|
+
await fs.mkdir(fileDir, { recursive: true });
|
|
75
|
+
await fs.writeFile(filePath, file.content, "utf-8");
|
|
76
|
+
printSystem(chalk.green(` āļø ${file.path}`));
|
|
77
|
+
}
|
|
78
|
+
return appDir;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function generateApplication(
|
|
82
|
+
description,
|
|
83
|
+
aiService,
|
|
84
|
+
cwd = process.cwd(),
|
|
85
|
+
) {
|
|
86
|
+
try {
|
|
87
|
+
printSystem(chalk.cyan("š¤ Claude is generating your application..."));
|
|
88
|
+
printSystem(chalk.gray(`Request: ${description}\n`));
|
|
89
|
+
|
|
90
|
+
printSystem(chalk.magenta("Agent Response: \n"));
|
|
91
|
+
|
|
92
|
+
const { object: application } = await generateObject({
|
|
93
|
+
model: aiService.model,
|
|
94
|
+
schema: ApplicationSchema,
|
|
95
|
+
prompt: `Create a complete, production-ready application for: ${description}
|
|
96
|
+
|
|
97
|
+
CRITICAL REQUIREMENTS:
|
|
98
|
+
1. Generate ALL files needed for the application to run
|
|
99
|
+
2. Include package.json with ALL dependencies and correct versions
|
|
100
|
+
3. Include README.md with setup instructions
|
|
101
|
+
4. Include configuration files (.gitignore, etc.)
|
|
102
|
+
5. Write clean, well-commented, production-ready code
|
|
103
|
+
6. Include error handling and input validation
|
|
104
|
+
7. Use modern JavaScript/TypeScript best practices
|
|
105
|
+
8. Make sure all imports and paths are correct
|
|
106
|
+
9. NO PLACEHOLDERS - everything must be complete and working
|
|
107
|
+
|
|
108
|
+
Provide:
|
|
109
|
+
- A meaningful kebab-case folder name
|
|
110
|
+
- All necessary files with complete content
|
|
111
|
+
- Setup commands (cd folder, npm install, npm run dev, etc.)
|
|
112
|
+
- All dependencies with versions
|
|
113
|
+
`,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
printSystem(chalk.green(`\n Generated: ${application.folderName} \n`));
|
|
117
|
+
printSystem(chalk.gray(`Description: ${application.description} \n`));
|
|
118
|
+
|
|
119
|
+
if (application.files.length === 0) {
|
|
120
|
+
throw new Error("No files were generated by the agent.");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
displayFileTree(application.files, application.folderName);
|
|
124
|
+
|
|
125
|
+
printSystem(chalk.cyan("\nš Creating files...\n"));
|
|
126
|
+
|
|
127
|
+
const appDir = await createApplicationFiles(
|
|
128
|
+
cwd,
|
|
129
|
+
application.folderName,
|
|
130
|
+
application.files,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
printSystem(chalk.cyan(`š Location: ${chalk.bold(appDir)}\n`));
|
|
134
|
+
|
|
135
|
+
if (application.setupCommands.length > 0) {
|
|
136
|
+
printSystem(chalk.cyan("Next Steps:\n"));
|
|
137
|
+
printSystem(chalk.white("```bash"));
|
|
138
|
+
|
|
139
|
+
application.setupCommands.forEach((cmd) => {
|
|
140
|
+
printSystem(chalk.white(cmd));
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
printSystem(chalk.white("```\n"));
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
folderName: application.folderName,
|
|
147
|
+
appDir,
|
|
148
|
+
files: application.files.map((f) => f.path),
|
|
149
|
+
commands: application.setupCommands,
|
|
150
|
+
success: true,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
} catch (error) {
|
|
154
|
+
printSystem(
|
|
155
|
+
chalk.red(`\nā Error generating application: ${error?.message}\n`),
|
|
156
|
+
);
|
|
157
|
+
if (error?.stack) {
|
|
158
|
+
printSystem(chalk.dim(error.stack + "\n"));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { google } from "@ai-sdk/google";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
|
|
4
|
+
export const avaiableTools = [
|
|
5
|
+
{
|
|
6
|
+
id: "google_search",
|
|
7
|
+
name: "Google Search",
|
|
8
|
+
description: "Search the web for up-to-date information.",
|
|
9
|
+
getTool: () => google.tools.googleSearch({}),
|
|
10
|
+
enabled: false,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "code_execution",
|
|
14
|
+
name: "Code Execution",
|
|
15
|
+
description:
|
|
16
|
+
"Generate and execute python code to perform calculations, solve problems, or provide accurate information.",
|
|
17
|
+
getTool: () => google.tools.codeExecution({}),
|
|
18
|
+
enabled: false,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "url_context",
|
|
22
|
+
name: "URL Context",
|
|
23
|
+
description: "Fetch and provide context from a given URL.",
|
|
24
|
+
getTool: () => google.tools.urlContext({}),
|
|
25
|
+
enabled: false,
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export const getEnabledTools = () => {
|
|
30
|
+
const tools = {};
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
for (const toolConfig of avaiableTools) {
|
|
34
|
+
if (toolConfig.enabled) {
|
|
35
|
+
tools[toolConfig.id] = toolConfig.getTool();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// DEBUG logging to verify which tools are enabled
|
|
40
|
+
if (Object.keys(tools).length > 0) {
|
|
41
|
+
console.log(
|
|
42
|
+
chalk.gray(`[DEBUG] Enabled tools: ${Object.keys(tools).join(", ")}`),
|
|
43
|
+
);
|
|
44
|
+
} else {
|
|
45
|
+
console.log(chalk.gray("[DEBUG] No tools enabled"));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return tools;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error(
|
|
51
|
+
chalk.red("[ERROR] Failed to initialize tools: ", error?.message),
|
|
52
|
+
);
|
|
53
|
+
console.error(
|
|
54
|
+
chalk.yellow("Make sure you have @ai-sdk/google version 2.0+ installed"),
|
|
55
|
+
);
|
|
56
|
+
console.error(chalk.yellow("Run: npm install @ai-sdk/google@latest"));
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export function toggleTool(toolId) {
|
|
62
|
+
const tool = avaiableTools.find((t) => t.id === toolId);
|
|
63
|
+
if (tool) {
|
|
64
|
+
tool.enabled = !tool.enabled;
|
|
65
|
+
console.log(
|
|
66
|
+
chalk.gray(`[DEBUG] Tool ${toolId} toggled to ${tool.enabled}`),
|
|
67
|
+
);
|
|
68
|
+
return tool.enabled;
|
|
69
|
+
}
|
|
70
|
+
console.log(chalk.red(`[DEBUG] Tool ${toolId} not found`));
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const enableTools = (toolIds) => {
|
|
75
|
+
console.log(chalk.gray(`[DEBUG] enableTools called with ${toolIds}`));
|
|
76
|
+
|
|
77
|
+
avaiableTools.forEach((tool) => {
|
|
78
|
+
const wasEnabled = tool.enabled;
|
|
79
|
+
tool.enabled = toolIds.includes(tool.id);
|
|
80
|
+
|
|
81
|
+
if (tool.enabled !== wasEnabled) {
|
|
82
|
+
console.log(
|
|
83
|
+
chalk.gray(`[DEBUG] ${tool.id} : ${wasEnabled} -> ${tool.enabled}`),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const enableCount = avaiableTools.filter((t) => t.enabled).length;
|
|
88
|
+
console.log(
|
|
89
|
+
chalk.gray(
|
|
90
|
+
`[DEBUG] Total tools enabled: ${enableCount}/${avaiableTools.length}`,
|
|
91
|
+
),
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export function getEnabledToolNames() {
|
|
97
|
+
const names = avaiableTools.filter((t) => t.enabled).map((t) => t.name);
|
|
98
|
+
console.log(chalk.gray("[DEBUG] getEnabledToolNames returning: "), names);
|
|
99
|
+
return names;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function resetTools() {
|
|
103
|
+
avaiableTools.forEach((tool) => (tool.enabled = false));
|
|
104
|
+
console.log(chalk.gray("[DEBUG] All tools have been reset (disabled)"));
|
|
105
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import { toNodeHandler } from "better-auth/node";
|
|
4
|
+
import dotenv from "dotenv";
|
|
5
|
+
import { auth } from "./lib/auth.js";
|
|
6
|
+
|
|
7
|
+
dotenv.config();
|
|
8
|
+
const app = express();
|
|
9
|
+
|
|
10
|
+
app.use(
|
|
11
|
+
cors({
|
|
12
|
+
origin: process.env.CORS_ORIGIN,
|
|
13
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
14
|
+
credentials: true,
|
|
15
|
+
exposedHeaders: ["set-cookie"],
|
|
16
|
+
}),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
app.all("/api/auth/*splat", toNodeHandler(auth));
|
|
20
|
+
|
|
21
|
+
app.use(express.json());
|
|
22
|
+
|
|
23
|
+
const PORT = process.env.PORT || 3000;
|
|
24
|
+
|
|
25
|
+
app.get("/", (req, res) => {
|
|
26
|
+
res.send("Hello World!");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
app.get("/device", async (req, res) => {
|
|
30
|
+
const { user_code } = req.query;
|
|
31
|
+
res.redirect(`${process.env.CORS_ORIGIN}/device?user_code=${user_code}`);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
app.listen(PORT, () => {
|
|
35
|
+
console.log(`Server is running on port ${PORT}`);
|
|
36
|
+
});
|
package/src/lib/auth.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
3
|
+
import { deviceAuthorization } from "better-auth/plugins";
|
|
4
|
+
import prisma from "./db.js";
|
|
5
|
+
|
|
6
|
+
const crossSiteCookieAttributes = {
|
|
7
|
+
sameSite: "none",
|
|
8
|
+
secure: true,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const auth = betterAuth({
|
|
12
|
+
database: prismaAdapter(prisma, {
|
|
13
|
+
provider: "postgresql",
|
|
14
|
+
}),
|
|
15
|
+
baseURL: process.env.BASE_URL,
|
|
16
|
+
trustedOrigins: [process.env.CORS_ORIGIN],
|
|
17
|
+
basePath: "/api/auth",
|
|
18
|
+
socialProviders: {
|
|
19
|
+
github: {
|
|
20
|
+
clientId: process.env.GITHUB_CLIENT_ID,
|
|
21
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
advanced: {
|
|
25
|
+
useSecureCookies: true,
|
|
26
|
+
defaultCookieAttributes: crossSiteCookieAttributes,
|
|
27
|
+
},
|
|
28
|
+
plugins: [
|
|
29
|
+
deviceAuthorization({
|
|
30
|
+
expiresIn: "30m",
|
|
31
|
+
interval: "5s",
|
|
32
|
+
}),
|
|
33
|
+
],
|
|
34
|
+
});
|
package/src/lib/db.js
ADDED
package/src/lib/token.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { CONFIG_DIR, TOKEN_FILE } from "../cli/commands/auth/login.js";
|
|
4
|
+
|
|
5
|
+
export async function getStoredToken() {
|
|
6
|
+
try {
|
|
7
|
+
const data = await fs.readFile(TOKEN_FILE, "utf-8");
|
|
8
|
+
const token = JSON.parse(data);
|
|
9
|
+
return token;
|
|
10
|
+
} catch (error) {
|
|
11
|
+
// File doesn't exist or can't be read
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function storeToken(token) {
|
|
17
|
+
try {
|
|
18
|
+
// Ensure the config directory exists
|
|
19
|
+
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
20
|
+
|
|
21
|
+
// store token with metadata
|
|
22
|
+
|
|
23
|
+
const tokenData = {
|
|
24
|
+
access_token: token.access_token,
|
|
25
|
+
refresh_token: token.refresh_token,
|
|
26
|
+
token_type: token.token_type || "Bearer",
|
|
27
|
+
scope: token.scope,
|
|
28
|
+
expires_at: token.expires_in
|
|
29
|
+
? new Date(Date.now() + token.expires_in * 1000).toISOString()
|
|
30
|
+
: null,
|
|
31
|
+
created_at: new Date().toISOString(),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
await fs.writeFile(TOKEN_FILE, JSON.stringify(tokenData, null, 2), "utf-8");
|
|
35
|
+
return true;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error(chalk.red("Failed to store token: "), error?.message);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function clearStoredToken() {
|
|
43
|
+
try {
|
|
44
|
+
await fs.unlink(TOKEN_FILE);
|
|
45
|
+
return true;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// File doesn't exist or can't be deleted
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function isTokenExpired() {
|
|
53
|
+
const token = await getStoredToken();
|
|
54
|
+
if (!token || !token.expires_at) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const expiresAt = new Date(token.expires_at);
|
|
59
|
+
const now = new Date();
|
|
60
|
+
|
|
61
|
+
return expiresAt.getTime() - now.getTime() < 5 * 60 * 1000;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function requireAuth() {
|
|
65
|
+
const token = await getStoredToken();
|
|
66
|
+
|
|
67
|
+
if (!token) {
|
|
68
|
+
console.log(
|
|
69
|
+
chalk.red(
|
|
70
|
+
'ā Not authenticated. Please run "claude-cli login" to authenticate.',
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (await isTokenExpired()) {
|
|
77
|
+
console.log(
|
|
78
|
+
chalk.yellow("ā ļø Your session has expired. Please log in again."),
|
|
79
|
+
);
|
|
80
|
+
console.log(chalk.gray(" Run: claude login"));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return token;
|
|
85
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import prisma from "../lib/db.js";
|
|
2
|
+
|
|
3
|
+
export class ChatService {
|
|
4
|
+
async createConversation(userId, mode = "chat", title = null) {
|
|
5
|
+
const conversation = await prisma.conversation.create({
|
|
6
|
+
data: {
|
|
7
|
+
title: title || `New ${mode} conversation`,
|
|
8
|
+
mode,
|
|
9
|
+
user: {
|
|
10
|
+
connect: { id: userId },
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
return conversation;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async getOrCreateConversations(userId, conversationId = null, mode = "chat") {
|
|
18
|
+
if (conversationId) {
|
|
19
|
+
const conversation = await prisma.conversation.findFirst({
|
|
20
|
+
where: {
|
|
21
|
+
id: conversationId,
|
|
22
|
+
userId,
|
|
23
|
+
},
|
|
24
|
+
include: {
|
|
25
|
+
messages: {
|
|
26
|
+
orderBy: {
|
|
27
|
+
createdAt: "asc",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
if (conversation) return conversation;
|
|
33
|
+
}
|
|
34
|
+
return await this.createConversation(userId, mode);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async addMessage(conversationId, role, content) {
|
|
38
|
+
const contentStr =
|
|
39
|
+
typeof content === "string" ? content : JSON.stringify(content);
|
|
40
|
+
const message = await prisma.message.create({
|
|
41
|
+
data: {
|
|
42
|
+
role,
|
|
43
|
+
content: contentStr,
|
|
44
|
+
conversation: {
|
|
45
|
+
connect: { id: conversationId },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
return message;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
parseContent(content) {
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(content);
|
|
55
|
+
} catch {
|
|
56
|
+
return content;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getMessages(conversationId) {
|
|
61
|
+
const messages = await prisma.message.findMany({
|
|
62
|
+
where: { conversationId },
|
|
63
|
+
orderBy: { createdAt: "asc" },
|
|
64
|
+
});
|
|
65
|
+
return messages.map((msg) => ({
|
|
66
|
+
...msg,
|
|
67
|
+
content: this.parseContent(msg.content),
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async getConversations(userId) {
|
|
72
|
+
const conversations = await prisma.conversation.findMany({
|
|
73
|
+
where: { userId },
|
|
74
|
+
orderBy: { updatedAt: "desc" },
|
|
75
|
+
include: {
|
|
76
|
+
messages: {
|
|
77
|
+
take: 1,
|
|
78
|
+
orderBy: { createdAt: "desc" },
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
return conversations;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async deleteConversation(conversationId, userId) {
|
|
86
|
+
return await prisma.conversation.delete({
|
|
87
|
+
where: { id: conversationId, userId },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async updateTitle(conversationId, title) {
|
|
92
|
+
return await prisma.conversation.update({
|
|
93
|
+
where: { id: conversationId },
|
|
94
|
+
data: { title },
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
formatMessagesForAI(messages) {
|
|
99
|
+
return messages.map((msg) => ({
|
|
100
|
+
role: msg.role,
|
|
101
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
}
|