add ai generations limit + random bug fixes
This commit is contained in:
parent
09ead6073b
commit
47ce5db223
@ -36,6 +36,13 @@
|
|||||||
"when": 1714565073180,
|
"when": 1714565073180,
|
||||||
"tag": "0004_cuddly_wolf_cub",
|
"tag": "0004_cuddly_wolf_cub",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 5,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1714950365718,
|
||||||
|
"tag": "0005_last_the_twelve",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import { ZodError, z } from "zod";
|
|||||||
|
|
||||||
import { user, sandbox, usersToSandboxes } from "./schema";
|
import { user, sandbox, usersToSandboxes } from "./schema";
|
||||||
import * as schema from "./schema";
|
import * as schema from "./schema";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq, sql } from "drizzle-orm";
|
||||||
|
|
||||||
export interface Env {
|
export interface Env {
|
||||||
DB: D1Database;
|
DB: D1Database;
|
||||||
@ -86,7 +86,6 @@ export default {
|
|||||||
|
|
||||||
const sb = await db.insert(sandbox).values({ type, name, userId, visibility }).returning().get();
|
const sb = await db.insert(sandbox).values({ type, name, userId, visibility }).returning().get();
|
||||||
|
|
||||||
// console.log("sb:", sb);
|
|
||||||
await fetch("https://storage.ishaan1013.workers.dev/api/init", {
|
await fetch("https://storage.ishaan1013.workers.dev/api/init", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ sandboxId: sb.id, type }),
|
body: JSON.stringify({ sandboxId: sb.id, type }),
|
||||||
@ -95,7 +94,6 @@ export default {
|
|||||||
|
|
||||||
return new Response(sb.id, { status: 200 });
|
return new Response(sb.id, { status: 200 });
|
||||||
} else {
|
} else {
|
||||||
console.log(method);
|
|
||||||
return methodNotAllowed;
|
return methodNotAllowed;
|
||||||
}
|
}
|
||||||
} else if (path === "/api/sandbox/share") {
|
} else if (path === "/api/sandbox/share") {
|
||||||
@ -162,12 +160,35 @@ export default {
|
|||||||
|
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const { sandboxId, userId } = deleteShareSchema.parse(body);
|
const { sandboxId, userId } = deleteShareSchema.parse(body);
|
||||||
console.log("DELETE", sandboxId, userId);
|
|
||||||
|
|
||||||
await db.delete(usersToSandboxes).where(and(eq(usersToSandboxes.userId, userId), eq(usersToSandboxes.sandboxId, sandboxId)));
|
await db.delete(usersToSandboxes).where(and(eq(usersToSandboxes.userId, userId), eq(usersToSandboxes.sandboxId, sandboxId)));
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
} else return methodNotAllowed;
|
} else return methodNotAllowed;
|
||||||
|
} else if (path === "/api/sandbox/generate" && method === "POST") {
|
||||||
|
const generateSchema = z.object({
|
||||||
|
userId: z.string(),
|
||||||
|
});
|
||||||
|
const body = await request.json();
|
||||||
|
const { userId } = generateSchema.parse(body);
|
||||||
|
|
||||||
|
const dbUser = await db.query.user.findFirst({
|
||||||
|
where: (user, { eq }) => eq(user.id, userId),
|
||||||
|
});
|
||||||
|
if (!dbUser) {
|
||||||
|
return new Response("User not found.", { status: 400 });
|
||||||
|
}
|
||||||
|
if (dbUser.generations !== null && dbUser.generations >= 30) {
|
||||||
|
return new Response("You reached the maximum # of generations.", { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
await db
|
||||||
|
.update(user)
|
||||||
|
.set({ generations: sql`${user.generations} + 1` })
|
||||||
|
.where(eq(user.id, userId))
|
||||||
|
.get();
|
||||||
|
|
||||||
|
return success;
|
||||||
} else if (path === "/api/user") {
|
} else if (path === "/api/user") {
|
||||||
if (method === "GET") {
|
if (method === "GET") {
|
||||||
const params = url.searchParams;
|
const params = url.searchParams;
|
||||||
|
@ -10,6 +10,7 @@ export const user = sqliteTable("user", {
|
|||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
image: text("image"),
|
image: text("image"),
|
||||||
|
generations: integer("generations").default(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type User = typeof user.$inferSelect;
|
export type User = typeof user.$inferSelect;
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
---
|
---
|
||||||
# Credit: Harkirat Singh https://github.com/hkirat/repl
|
|
||||||
|
|
||||||
# Source: ingress-nginx/templates/controller-serviceaccount.yaml
|
# Source: ingress-nginx/templates/controller-serviceaccount.yaml
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
|
47
backend/server/dist/index.js
vendored
47
backend/server/dist/index.js
vendored
@ -65,14 +65,14 @@ io.use((socket, next) => __awaiter(void 0, void 0, void 0, function* () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
socket.data = {
|
socket.data = {
|
||||||
id: sandboxId,
|
|
||||||
userId,
|
userId,
|
||||||
|
sandboxId: sandboxId,
|
||||||
};
|
};
|
||||||
next();
|
next();
|
||||||
}));
|
}));
|
||||||
io.on("connection", (socket) => __awaiter(void 0, void 0, void 0, function* () {
|
io.on("connection", (socket) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
const data = socket.data;
|
const data = socket.data;
|
||||||
const sandboxFiles = yield (0, utils_1.getSandboxFiles)(data.id);
|
const sandboxFiles = yield (0, utils_1.getSandboxFiles)(data.sandboxId);
|
||||||
sandboxFiles.fileData.forEach((file) => {
|
sandboxFiles.fileData.forEach((file) => {
|
||||||
const filePath = path_1.default.join(dirName, file.id);
|
const filePath = path_1.default.join(dirName, file.id);
|
||||||
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
||||||
@ -113,7 +113,7 @@ io.on("connection", (socket) => __awaiter(void 0, void 0, void 0, function* () {
|
|||||||
socket.on("createFile", (name) => __awaiter(void 0, void 0, void 0, function* () {
|
socket.on("createFile", (name) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
yield ratelimit_1.createFileRL.consume(data.userId, 1);
|
yield ratelimit_1.createFileRL.consume(data.userId, 1);
|
||||||
const id = `projects/${data.id}/${name}`;
|
const id = `projects/${data.sandboxId}/${name}`;
|
||||||
fs_1.default.writeFile(path_1.default.join(dirName, id), "", function (err) {
|
fs_1.default.writeFile(path_1.default.join(dirName, id), "", function (err) {
|
||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
@ -165,7 +165,7 @@ io.on("connection", (socket) => __awaiter(void 0, void 0, void 0, function* () {
|
|||||||
});
|
});
|
||||||
sandboxFiles.fileData = sandboxFiles.fileData.filter((f) => f.id !== fileId);
|
sandboxFiles.fileData = sandboxFiles.fileData.filter((f) => f.id !== fileId);
|
||||||
yield (0, utils_1.deleteFile)(fileId);
|
yield (0, utils_1.deleteFile)(fileId);
|
||||||
const newFiles = yield (0, utils_1.getSandboxFiles)(data.id);
|
const newFiles = yield (0, utils_1.getSandboxFiles)(data.sandboxId);
|
||||||
callback(newFiles.files);
|
callback(newFiles.files);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
@ -184,7 +184,7 @@ io.on("connection", (socket) => __awaiter(void 0, void 0, void 0, function* () {
|
|||||||
const pty = (0, node_pty_1.spawn)(os_1.default.platform() === "win32" ? "cmd.exe" : "bash", [], {
|
const pty = (0, node_pty_1.spawn)(os_1.default.platform() === "win32" ? "cmd.exe" : "bash", [], {
|
||||||
name: "xterm",
|
name: "xterm",
|
||||||
cols: 100,
|
cols: 100,
|
||||||
cwd: path_1.default.join(dirName, "projects", data.id),
|
cwd: path_1.default.join(dirName, "projects", data.sandboxId),
|
||||||
});
|
});
|
||||||
const onData = pty.onData((data) => {
|
const onData = pty.onData((data) => {
|
||||||
socket.emit("terminalResponse", {
|
socket.emit("terminalResponse", {
|
||||||
@ -213,37 +213,26 @@ io.on("connection", (socket) => __awaiter(void 0, void 0, void 0, function* () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
socket.on("generateCode", (fileName, code, line, instructions, callback) => __awaiter(void 0, void 0, void 0, function* () {
|
socket.on("generateCode", (fileName, code, line, instructions, callback) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
console.log("Generating code...");
|
const fetchPromise = fetch(`http://localhost:8787/api/sandbox/generate`, {
|
||||||
const res = yield fetch(`https://api.cloudflare.com/client/v4/accounts/${process.env.CF_USER_ID}/ai/run/@cf/meta/llama-3-8b-instruct`, {
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${process.env.CF_API_TOKEN}`,
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
messages: [
|
userId: data.userId,
|
||||||
{
|
|
||||||
role: "system",
|
|
||||||
content: "You are an expert coding assistant. You read code from a file, and you suggest new code to add to the file. You may be given instructions on what to generate, which you should follow. You should generate code that is correct, efficient, and follows best practices. You should also generate code that is clear and easy to read. When you generate code, you should only return the code, and nothing else. You should not include backticks in the code you generate.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
content: `The file is called ${fileName}.`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
content: `Here are my instructions on what to generate: ${instructions}.`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
content: `Suggest me code to insert at line ${line} in my file. Give only the code, and NOTHING else. DO NOT include backticks in your response. My code file content is as follows
|
|
||||||
|
|
||||||
${code}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
const json = yield res.json();
|
const generateCodePromise = (0, utils_1.generateCode)({
|
||||||
|
fileName,
|
||||||
|
code,
|
||||||
|
line,
|
||||||
|
instructions,
|
||||||
|
});
|
||||||
|
const [fetchResponse, generateCodeResponse] = yield Promise.all([
|
||||||
|
fetchPromise,
|
||||||
|
generateCodePromise,
|
||||||
|
]);
|
||||||
|
const json = yield generateCodeResponse.json();
|
||||||
callback(json);
|
callback(json);
|
||||||
}));
|
}));
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
|
34
backend/server/dist/utils.js
vendored
34
backend/server/dist/utils.js
vendored
@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.deleteFile = exports.saveFile = exports.renameFile = exports.createFile = exports.getSandboxFiles = void 0;
|
exports.generateCode = exports.deleteFile = exports.saveFile = exports.renameFile = exports.createFile = exports.getSandboxFiles = void 0;
|
||||||
const getSandboxFiles = (id) => __awaiter(void 0, void 0, void 0, function* () {
|
const getSandboxFiles = (id) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
const sandboxRes = yield fetch(`https://storage.ishaan1013.workers.dev/api?sandboxId=${id}`);
|
const sandboxRes = yield fetch(`https://storage.ishaan1013.workers.dev/api?sandboxId=${id}`);
|
||||||
const sandboxData = yield sandboxRes.json();
|
const sandboxData = yield sandboxRes.json();
|
||||||
@ -121,3 +121,35 @@ const deleteFile = (fileId) => __awaiter(void 0, void 0, void 0, function* () {
|
|||||||
return res.ok;
|
return res.ok;
|
||||||
});
|
});
|
||||||
exports.deleteFile = deleteFile;
|
exports.deleteFile = deleteFile;
|
||||||
|
const generateCode = (_a) => __awaiter(void 0, [_a], void 0, function* ({ fileName, code, line, instructions, }) {
|
||||||
|
return yield fetch(`https://api.cloudflare.com/client/v4/accounts/${process.env.CF_USER_ID}/ai/run/@cf/meta/llama-3-8b-instruct`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${process.env.CF_API_TOKEN}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "system",
|
||||||
|
content: "You are an expert coding assistant. You read code from a file, and you suggest new code to add to the file. You may be given instructions on what to generate, which you should follow. You should generate code that is correct, efficient, and follows best practices. You should also generate code that is clear and easy to read. When you generate code, you should only return the code, and nothing else. You should not include backticks in the code you generate.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: `The file is called ${fileName}.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: `Here are my instructions on what to generate: ${instructions}.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: `Suggest me code to insert at line ${line} in my file. Give only the code, and NOTHING else. DO NOT include backticks in your response. My code file content is as follows
|
||||||
|
|
||||||
|
${code}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
exports.generateCode = generateCode;
|
||||||
|
@ -11,6 +11,7 @@ import { User } from "./types"
|
|||||||
import {
|
import {
|
||||||
createFile,
|
createFile,
|
||||||
deleteFile,
|
deleteFile,
|
||||||
|
generateCode,
|
||||||
getSandboxFiles,
|
getSandboxFiles,
|
||||||
renameFile,
|
renameFile,
|
||||||
saveFile,
|
saveFile,
|
||||||
@ -81,8 +82,8 @@ io.use(async (socket, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
socket.data = {
|
socket.data = {
|
||||||
id: sandboxId,
|
|
||||||
userId,
|
userId,
|
||||||
|
sandboxId: sandboxId,
|
||||||
}
|
}
|
||||||
|
|
||||||
next()
|
next()
|
||||||
@ -91,10 +92,10 @@ io.use(async (socket, next) => {
|
|||||||
io.on("connection", async (socket) => {
|
io.on("connection", async (socket) => {
|
||||||
const data = socket.data as {
|
const data = socket.data as {
|
||||||
userId: string
|
userId: string
|
||||||
id: string
|
sandboxId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const sandboxFiles = await getSandboxFiles(data.id)
|
const sandboxFiles = await getSandboxFiles(data.sandboxId)
|
||||||
sandboxFiles.fileData.forEach((file) => {
|
sandboxFiles.fileData.forEach((file) => {
|
||||||
const filePath = path.join(dirName, file.id)
|
const filePath = path.join(dirName, file.id)
|
||||||
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
||||||
@ -142,7 +143,7 @@ io.on("connection", async (socket) => {
|
|||||||
try {
|
try {
|
||||||
await createFileRL.consume(data.userId, 1)
|
await createFileRL.consume(data.userId, 1)
|
||||||
|
|
||||||
const id = `projects/${data.id}/${name}`
|
const id = `projects/${data.sandboxId}/${name}`
|
||||||
|
|
||||||
fs.writeFile(path.join(dirName, id), "", function (err) {
|
fs.writeFile(path.join(dirName, id), "", function (err) {
|
||||||
if (err) throw err
|
if (err) throw err
|
||||||
@ -206,7 +207,7 @@ io.on("connection", async (socket) => {
|
|||||||
|
|
||||||
await deleteFile(fileId)
|
await deleteFile(fileId)
|
||||||
|
|
||||||
const newFiles = await getSandboxFiles(data.id)
|
const newFiles = await getSandboxFiles(data.sandboxId)
|
||||||
callback(newFiles.files)
|
callback(newFiles.files)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
socket.emit("rateLimit", "Rate limited: file deletion. Please slow down.")
|
socket.emit("rateLimit", "Rate limited: file deletion. Please slow down.")
|
||||||
@ -226,7 +227,7 @@ io.on("connection", async (socket) => {
|
|||||||
const pty = spawn(os.platform() === "win32" ? "cmd.exe" : "bash", [], {
|
const pty = spawn(os.platform() === "win32" ? "cmd.exe" : "bash", [], {
|
||||||
name: "xterm",
|
name: "xterm",
|
||||||
cols: 100,
|
cols: 100,
|
||||||
cwd: path.join(dirName, "projects", data.id),
|
cwd: path.join(dirName, "projects", data.sandboxId),
|
||||||
})
|
})
|
||||||
|
|
||||||
const onData = pty.onData((data) => {
|
const onData = pty.onData((data) => {
|
||||||
@ -269,42 +270,29 @@ io.on("connection", async (socket) => {
|
|||||||
instructions: string,
|
instructions: string,
|
||||||
callback
|
callback
|
||||||
) => {
|
) => {
|
||||||
console.log("Generating code...")
|
const fetchPromise = fetch(`http://localhost:8787/api/sandbox/generate`, {
|
||||||
const res = await fetch(
|
|
||||||
`https://api.cloudflare.com/client/v4/accounts/${process.env.CF_USER_ID}/ai/run/@cf/meta/llama-3-8b-instruct`,
|
|
||||||
{
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${process.env.CF_API_TOKEN}`,
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
messages: [
|
userId: data.userId,
|
||||||
{
|
|
||||||
role: "system",
|
|
||||||
content:
|
|
||||||
"You are an expert coding assistant. You read code from a file, and you suggest new code to add to the file. You may be given instructions on what to generate, which you should follow. You should generate code that is correct, efficient, and follows best practices. You should also generate code that is clear and easy to read. When you generate code, you should only return the code, and nothing else. You should not include backticks in the code you generate.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
content: `The file is called ${fileName}.`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
content: `Here are my instructions on what to generate: ${instructions}.`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
content: `Suggest me code to insert at line ${line} in my file. Give only the code, and NOTHING else. DO NOT include backticks in your response. My code file content is as follows
|
|
||||||
|
|
||||||
${code}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
}),
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
const json = await res.json()
|
const generateCodePromise = generateCode({
|
||||||
|
fileName,
|
||||||
|
code,
|
||||||
|
line,
|
||||||
|
instructions,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [fetchResponse, generateCodeResponse] = await Promise.all([
|
||||||
|
fetchPromise,
|
||||||
|
generateCodePromise,
|
||||||
|
])
|
||||||
|
|
||||||
|
const json = await generateCodeResponse.json()
|
||||||
callback(json)
|
callback(json)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -4,11 +4,9 @@ export type User = {
|
|||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
|
generations: number
|
||||||
sandbox: Sandbox[]
|
sandbox: Sandbox[]
|
||||||
usersToSandboxes: {
|
usersToSandboxes: UsersToSandboxes[]
|
||||||
userId: string
|
|
||||||
sandboxId: string
|
|
||||||
}[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Sandbox = {
|
export type Sandbox = {
|
||||||
@ -17,10 +15,12 @@ export type Sandbox = {
|
|||||||
type: "react" | "node"
|
type: "react" | "node"
|
||||||
visibility: "public" | "private"
|
visibility: "public" | "private"
|
||||||
userId: string
|
userId: string
|
||||||
usersToSandboxes: {
|
usersToSandboxes: UsersToSandboxes[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UsersToSandboxes = {
|
||||||
userId: string
|
userId: string
|
||||||
sandboxId: string
|
sandboxId: string
|
||||||
}[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TFolder = {
|
export type TFolder = {
|
||||||
|
@ -134,3 +134,49 @@ export const deleteFile = async (fileId: string) => {
|
|||||||
})
|
})
|
||||||
return res.ok
|
return res.ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const generateCode = async ({
|
||||||
|
fileName,
|
||||||
|
code,
|
||||||
|
line,
|
||||||
|
instructions,
|
||||||
|
}: {
|
||||||
|
fileName: string
|
||||||
|
code: string
|
||||||
|
line: number
|
||||||
|
instructions: string
|
||||||
|
}) => {
|
||||||
|
return await fetch(
|
||||||
|
`https://api.cloudflare.com/client/v4/accounts/${process.env.CF_USER_ID}/ai/run/@cf/meta/llama-3-8b-instruct`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${process.env.CF_API_TOKEN}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "system",
|
||||||
|
content:
|
||||||
|
"You are an expert coding assistant. You read code from a file, and you suggest new code to add to the file. You may be given instructions on what to generate, which you should follow. You should generate code that is correct, efficient, and follows best practices. You should also generate code that is clear and easy to read. When you generate code, you should only return the code, and nothing else. You should not include backticks in the code you generate.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: `The file is called ${fileName}.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: `Here are my instructions on what to generate: ${instructions}.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: `Suggest me code to insert at line ${line} in my file. Give only the code, and NOTHING else. DO NOT include backticks in your response. My code file content is as follows
|
||||||
|
|
||||||
|
${code}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -5,9 +5,13 @@ import { Button } from "../ui/button"
|
|||||||
import { Check, Loader2, RotateCw, Sparkles, X } from "lucide-react"
|
import { Check, Loader2, RotateCw, Sparkles, X } from "lucide-react"
|
||||||
import { Socket } from "socket.io-client"
|
import { Socket } from "socket.io-client"
|
||||||
import { Editor } from "@monaco-editor/react"
|
import { Editor } from "@monaco-editor/react"
|
||||||
|
import { User } from "@/lib/types"
|
||||||
|
import { toast } from "sonner"
|
||||||
|
import { usePathname, useRouter } from "next/navigation"
|
||||||
// import monaco from "monaco-editor"
|
// import monaco from "monaco-editor"
|
||||||
|
|
||||||
export default function GenerateInput({
|
export default function GenerateInput({
|
||||||
|
user,
|
||||||
socket,
|
socket,
|
||||||
width,
|
width,
|
||||||
data,
|
data,
|
||||||
@ -15,6 +19,7 @@ export default function GenerateInput({
|
|||||||
onExpand,
|
onExpand,
|
||||||
onAccept,
|
onAccept,
|
||||||
}: {
|
}: {
|
||||||
|
user: User
|
||||||
socket: Socket
|
socket: Socket
|
||||||
width: number
|
width: number
|
||||||
data: {
|
data: {
|
||||||
@ -28,6 +33,8 @@ export default function GenerateInput({
|
|||||||
onExpand: () => void
|
onExpand: () => void
|
||||||
onAccept: (code: string) => void
|
onAccept: (code: string) => void
|
||||||
}) {
|
}) {
|
||||||
|
const pathname = usePathname()
|
||||||
|
const router = useRouter()
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
const [code, setCode] = useState("")
|
const [code, setCode] = useState("")
|
||||||
@ -50,6 +57,12 @@ export default function GenerateInput({
|
|||||||
}: {
|
}: {
|
||||||
regenerate?: boolean
|
regenerate?: boolean
|
||||||
}) => {
|
}) => {
|
||||||
|
if (user.generations >= 30) {
|
||||||
|
toast.error(
|
||||||
|
"You reached the maximum # of generations. Contact @ishaandey_ on X/Twitter to reset :)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
setLoading({ generate: !regenerate, regenerate })
|
setLoading({ generate: !regenerate, regenerate })
|
||||||
setCurrentPrompt(input)
|
setCurrentPrompt(input)
|
||||||
socket.emit(
|
socket.emit(
|
||||||
@ -72,6 +85,7 @@ export default function GenerateInput({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCode(res.result.response)
|
setCode(res.result.response)
|
||||||
|
router.refresh()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,7 @@ import Sidebar from "./sidebar"
|
|||||||
import EditorTerminal from "./terminal"
|
import EditorTerminal from "./terminal"
|
||||||
import { Button } from "../ui/button"
|
import { Button } from "../ui/button"
|
||||||
import GenerateInput from "./generate"
|
import GenerateInput from "./generate"
|
||||||
import { TFile, TFileData, TFolder, TTab } from "./sidebar/types"
|
import { Sandbox, User, TFile, TFileData, TFolder, TTab } from "@/lib/types"
|
||||||
import { Sandbox, User } from "@/lib/types"
|
|
||||||
import { processFileType, validateName } from "@/lib/utils"
|
import { processFileType, validateName } from "@/lib/utils"
|
||||||
import { Cursors } from "./live/cursors"
|
import { Cursors } from "./live/cursors"
|
||||||
|
|
||||||
@ -455,6 +454,7 @@ export default function CodeEditor({
|
|||||||
<div className="z-50 p-1" ref={generateWidgetRef}>
|
<div className="z-50 p-1" ref={generateWidgetRef}>
|
||||||
{generate.show && ai ? (
|
{generate.show && ai ? (
|
||||||
<GenerateInput
|
<GenerateInput
|
||||||
|
user={userData}
|
||||||
socket={socket}
|
socket={socket}
|
||||||
width={generate.width - 90}
|
width={generate.width - 90}
|
||||||
data={{
|
data={{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { getIconForFile } from "vscode-icons-js"
|
import { getIconForFile } from "vscode-icons-js"
|
||||||
import { TFile, TTab } from "./types"
|
import { TFile, TTab } from "@/lib/types"
|
||||||
import { useEffect, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
import {
|
import {
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { useEffect, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
import { getIconForFolder, getIconForOpenFolder } from "vscode-icons-js"
|
import { getIconForFolder, getIconForOpenFolder } from "vscode-icons-js"
|
||||||
import { TFile, TFolder, TTab } from "./types"
|
import { TFile, TFolder, TTab } from "@/lib/types"
|
||||||
import SidebarFile from "./file"
|
import SidebarFile from "./file"
|
||||||
import {
|
import {
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { FilePlus, FolderPlus, Loader2, Search, Sparkles } from "lucide-react"
|
import { FilePlus, FolderPlus, Loader2, Search, Sparkles } from "lucide-react"
|
||||||
import SidebarFile from "./file"
|
import SidebarFile from "./file"
|
||||||
import SidebarFolder from "./folder"
|
import SidebarFolder from "./folder"
|
||||||
import { TFile, TFolder, TTab } from "./types"
|
import { TFile, TFolder, TTab } from "@/lib/types"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import New from "./new"
|
import New from "./new"
|
||||||
import { Socket } from "socket.io-client"
|
import { Socket } from "socket.io-client"
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
export type TFolder = {
|
|
||||||
id: string
|
|
||||||
type: "folder"
|
|
||||||
name: string
|
|
||||||
children: (TFile | TFolder)[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TFile = {
|
|
||||||
id: string
|
|
||||||
type: "file"
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TTab = TFile & {
|
|
||||||
saved: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TFileData = {
|
|
||||||
id: string
|
|
||||||
data: string
|
|
||||||
}
|
|
@ -10,7 +10,7 @@ import {
|
|||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
import { User } from "@/lib/types"
|
import { User } from "@/lib/types"
|
||||||
import { useClerk } from "@clerk/nextjs"
|
import { useClerk } from "@clerk/nextjs"
|
||||||
import { LogOut, Pencil } from "lucide-react"
|
import { LogOut, Pencil, Sparkles } from "lucide-react"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
|
|
||||||
export default function UserButton({ userData }: { userData: User }) {
|
export default function UserButton({ userData }: { userData: User }) {
|
||||||
@ -29,7 +29,7 @@ export default function UserButton({ userData }: { userData: User }) {
|
|||||||
.map((name) => name[0].toUpperCase())}
|
.map((name) => name[0].toUpperCase())}
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="w-40" align="end">
|
<DropdownMenuContent className="w-48" align="end">
|
||||||
<div className="py-1.5 px-2 w-full">
|
<div className="py-1.5 px-2 w-full">
|
||||||
<div className="font-medium">{userData.name}</div>
|
<div className="font-medium">{userData.name}</div>
|
||||||
<div className="text-sm w-full overflow-hidden text-ellipsis whitespace-nowrap text-muted-foreground">
|
<div className="text-sm w-full overflow-hidden text-ellipsis whitespace-nowrap text-muted-foreground">
|
||||||
@ -37,6 +37,21 @@ export default function UserButton({ userData }: { userData: User }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<div className="py-1.5 px-2 w-full flex flex-col items-start text-sm">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Sparkles className={`h-4 w-4 mr-2 text-indigo-500`} />
|
||||||
|
AI Usage: {userData.generations}/30
|
||||||
|
</div>
|
||||||
|
<div className="rounded-full w-full mt-2 h-2 overflow-hidden bg-secondary">
|
||||||
|
<div
|
||||||
|
className="h-full bg-indigo-500 rounded-full"
|
||||||
|
style={{
|
||||||
|
width: `${(userData.generations * 100) / 30}%`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem className="cursor-pointer">
|
<DropdownMenuItem className="cursor-pointer">
|
||||||
<Pencil className="mr-2 h-4 w-4" />
|
<Pencil className="mr-2 h-4 w-4" />
|
||||||
|
@ -4,6 +4,7 @@ export type User = {
|
|||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
|
generations: number
|
||||||
sandbox: Sandbox[]
|
sandbox: Sandbox[]
|
||||||
usersToSandboxes: UsersToSandboxes[]
|
usersToSandboxes: UsersToSandboxes[]
|
||||||
}
|
}
|
||||||
@ -38,3 +39,25 @@ export type R2FileData = {
|
|||||||
version: string
|
version: string
|
||||||
key: string
|
key: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TFolder = {
|
||||||
|
id: string
|
||||||
|
type: "folder"
|
||||||
|
name: string
|
||||||
|
children: (TFile | TFolder)[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TFile = {
|
||||||
|
id: string
|
||||||
|
type: "file"
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TTab = TFile & {
|
||||||
|
saved: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TFileData = {
|
||||||
|
id: string
|
||||||
|
data: string
|
||||||
|
}
|
||||||
|
98
frontend/package-lock.json
generated
98
frontend/package-lock.json
generated
@ -54,6 +54,8 @@
|
|||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
|
"postcss-import": "^16.1.0",
|
||||||
|
"postcss-nesting": "^12.1.2",
|
||||||
"tailwindcss": "^3.3.0",
|
"tailwindcss": "^3.3.0",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
@ -247,6 +249,50 @@
|
|||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
|
||||||
"integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
|
"integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@csstools/selector-resolve-nested": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-uWvSaeRcHyeNenKg8tp17EVDRkpflmdyvbE0DHo6D/GdBb6PDnCYYU6gRpXhtICMGMcahQmj2zGxwFM/WC8hCg==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/csstools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/csstools"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^14 || ^16 || >=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"postcss-selector-parser": "^6.0.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@csstools/selector-specificity": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-KEPNw4+WW5AVEIyzC80rTbWEUatTW2lXpN8+8ILC8PiPeWPjwUzrPZDIOZ2wwqDmeqOYTdSGyL3+vE5GC3FB3Q==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/csstools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/csstools"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^14 || ^16 || >=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"postcss-selector-parser": "^6.0.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@floating-ui/core": {
|
"node_modules/@floating-ui/core": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz",
|
||||||
@ -2810,16 +2856,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-import": {
|
"node_modules/postcss-import": {
|
||||||
"version": "15.1.0",
|
"version": "16.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-16.1.0.tgz",
|
||||||
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
"integrity": "sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"postcss-value-parser": "^4.0.0",
|
"postcss-value-parser": "^4.0.0",
|
||||||
"read-cache": "^1.0.0",
|
"read-cache": "^1.0.0",
|
||||||
"resolve": "^1.1.7"
|
"resolve": "^1.1.7"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"postcss": "^8.0.0"
|
"postcss": "^8.0.0"
|
||||||
@ -2906,6 +2953,33 @@
|
|||||||
"postcss": "^8.2.14"
|
"postcss": "^8.2.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/postcss-nesting": {
|
||||||
|
"version": "12.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.2.tgz",
|
||||||
|
"integrity": "sha512-FUmTHGDNundodutB4PUBxt/EPuhgtpk8FJGRsBhOuy+6FnkR2A8RZWIsyyy6XmhvX2DZQQWIkvu+HB4IbJm+Ew==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/csstools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/csstools"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@csstools/selector-resolve-nested": "^1.1.0",
|
||||||
|
"@csstools/selector-specificity": "^3.0.3",
|
||||||
|
"postcss-selector-parser": "^6.0.13"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14 || ^16 || >=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"postcss": "^8.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss-selector-parser": {
|
"node_modules/postcss-selector-parser": {
|
||||||
"version": "6.0.16",
|
"version": "6.0.16",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
|
||||||
@ -3476,6 +3550,22 @@
|
|||||||
"tailwindcss": ">=3.0.0 || insiders"
|
"tailwindcss": ">=3.0.0 || insiders"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwindcss/node_modules/postcss-import": {
|
||||||
|
"version": "15.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
||||||
|
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
||||||
|
"dependencies": {
|
||||||
|
"postcss-value-parser": "^4.0.0",
|
||||||
|
"read-cache": "^1.0.0",
|
||||||
|
"resolve": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"postcss": "^8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/thenify": {
|
"node_modules/thenify": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||||
|
@ -55,6 +55,8 @@
|
|||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
|
"postcss-import": "^16.1.0",
|
||||||
|
"postcss-nesting": "^12.1.2",
|
||||||
"tailwindcss": "^3.3.0",
|
"tailwindcss": "^3.3.0",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: [
|
||||||
tailwindcss: {},
|
"postcss-import",
|
||||||
autoprefixer: {},
|
"tailwindcss/nesting",
|
||||||
},
|
"postcss-nesting",
|
||||||
};
|
"autoprefixer",
|
||||||
|
"tailwindcss",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user