+
diff --git a/mobile-magic/apps/frontend/components/GitHubIcon.tsx b/mobile-magic/apps/frontend/components/GitHubIcon.tsx
new file mode 100644
index 0000000..f65c3c1
--- /dev/null
+++ b/mobile-magic/apps/frontend/components/GitHubIcon.tsx
@@ -0,0 +1,13 @@
+export function GitHubIcon() {
+ return (
+
+ );
+}
diff --git a/mobile-magic/apps/frontend/components/GitHubModal.tsx b/mobile-magic/apps/frontend/components/GitHubModal.tsx
new file mode 100644
index 0000000..162090a
--- /dev/null
+++ b/mobile-magic/apps/frontend/components/GitHubModal.tsx
@@ -0,0 +1,298 @@
+import React, { useEffect, useState } from "react";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import { Button } from "@/components/ui/button";
+import { Copy, ExternalLink } from "lucide-react";
+import { GitHubIcon } from "./GitHubIcon";
+import axios from "axios";
+import { useAuth } from "@clerk/nextjs";
+import { useRouter } from "next/navigation";
+
+export const GitHubModal: React.FC<{
+ projectId: string;
+ workerUrl: string;
+}> = ({ projectId, workerUrl }) => {
+ const { getToken } = useAuth();
+ const [isConnected, setIsConnected] = useState(false);
+ const [isRepoCreated, setIsRepoCreated] = useState(false);
+ const [repoUrl, setRepoUrl] = useState
(null);
+ const [gitHubUsername, setGitHubUsername] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const router = useRouter();
+ const [copied, setCopied] = useState(false);
+ const [cloneUrl, setCloneUrl] = useState(null);
+ const branch = "main";
+
+ useEffect(() => {
+ (async () => {
+ const code = new URLSearchParams(window.location.search).get("code");
+ if (code) {
+ setIsLoading(true);
+ try {
+ const response = await axios.post(
+ `${process.env.NEXT_PUBLIC_BACKEND_URL}/github/token`,
+ {
+ code,
+ redirectUri: `${window.location.origin}/project/${projectId}`,
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${await getToken()}`,
+ },
+ }
+ );
+
+ if (response.status != 200) {
+ console.log("Error exchanging code to access token");
+ return;
+ }
+ setGitHubUsername(response.data.username);
+ router.push(`/project/${projectId}`);
+ } catch (err) {
+ console.log("GitHub OAuth error:", err);
+ } finally {
+ setIsLoading(false);
+ }
+ } else {
+ setIsLoading(true);
+ try {
+ const response = await axios.get(
+ `${process.env.NEXT_PUBLIC_BACKEND_URL}/github/repoUrl?projectId=${projectId}`,
+ {
+ headers: {
+ Authorization: `Bearer ${await getToken()}`,
+ },
+ }
+ );
+ if (response.status == 200) {
+ setRepoUrl(response.data.repoUrl);
+ setCloneUrl(getCloneUrl(response.data.repoUrl, "HTTPS"));
+ }
+ } catch (error) {
+ console.log(error);
+ } finally {
+ setIsLoading(false);
+ }
+ }
+ })();
+ }, []);
+
+ useEffect(() => {
+ setIsRepoCreated(!!repoUrl);
+ setIsConnected(!!gitHubUsername);
+ }, [repoUrl, gitHubUsername]);
+
+ function copyToClipboard(text: string) {
+ navigator.clipboard.writeText(text);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ }
+
+ async function handleOnConnect() {
+ setIsLoading(true);
+ try {
+ const response = await axios.get(
+ `${process.env.NEXT_PUBLIC_BACKEND_URL}/github/username`,
+ {
+ headers: {
+ Authorization: `Bearer ${await getToken()}`,
+ },
+ }
+ );
+ if (response.status == 200) {
+ if (response.data.isConnected)
+ setGitHubUsername(response.data.gitHubUsername);
+ else {
+ const redirectUri = encodeURIComponent(
+ `${window.location.origin}/project/${projectId}`
+ );
+ const authUrl = `https://github.com/login/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID}&redirect_uri=${redirectUri}&scope=repo`;
+ window.location.href = authUrl;
+ }
+ }
+ } catch (error) {
+ console.log(error);
+ } finally {
+ setIsLoading(false);
+ }
+ }
+
+ async function handleCreateRepo() {
+ setIsLoading(true);
+ try {
+ const response = await axios.post(
+ `${process.env.NEXT_PUBLIC_BACKEND_URL}/github/createRepo`,
+ {
+ projectId,
+ workerUrl,
+ },
+ {
+ headers: {
+ Authorization: `Bearer ${await getToken()}`,
+ },
+ }
+ );
+ if (response.status == 200) {
+ setRepoUrl(`${response.data.repoUrl}`);
+ setCloneUrl(getCloneUrl(response.data.repoUrl, "HTTPS"));
+ }
+ } catch (error) {
+ console.log(error);
+ } finally {
+ setIsLoading(false);
+ }
+ }
+
+ return (
+
+
+
+
+
+ {isLoading ? (
+
+ ) : isRepoCreated ? (
+
+
GitHub
+
+
This project is connected to
+
+ {repoUrl?.split("/").slice(-2).join("/") || ""}.
+
+
+ Mobile Magic will commit changes to the{" "}
+ {branch} branch.
+
+
+
+
+
Clone
+
+
+
+
+
+
+
+
+ {cloneUrl}
+
+
+
+ {copied && (
+
+ Copied
+
+ )}
+
+
+
+
+
+
+ ) : isConnected ? (
+
+
GitHub
+
+ Connect your project to GitHub to save your code and collaborate
+ with others.
+
+
Create in
+
+
+ ) : (
+
+
GitHub
+
+ Connect your project to GitHub to save your code and collaborate
+ with others.
+
+
+
+ )}
+
+
+ );
+};
+
+function LoadingSpinner() {
+ return (
+
+ );
+}
+
+function getCloneUrl(repoUrl: string, type: "HTTPS" | "SSH" | "CLI"): string {
+ const usernameRepoName = repoUrl.split("/").slice(-2).join("/");
+ if (type == "HTTPS") return `${repoUrl}.git`;
+ else if (type == "SSH") return `git@github.com:${usernameRepoName}.git`;
+ else return `gh repo clone ${usernameRepoName}`;
+}
+// bg-blue-600 hover:bg-blue-700
diff --git a/mobile-magic/apps/frontend/components/ui/popover.tsx b/mobile-magic/apps/frontend/components/ui/popover.tsx
new file mode 100644
index 0000000..ca6d8bb
--- /dev/null
+++ b/mobile-magic/apps/frontend/components/ui/popover.tsx
@@ -0,0 +1,48 @@
+"use client"
+
+import * as React from "react"
+import * as PopoverPrimitive from "@radix-ui/react-popover"
+
+import { cn } from "@/lib/utils"
+
+function Popover({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function PopoverTrigger({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function PopoverContent({
+ className,
+ align = "center",
+ sideOffset = 4,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+function PopoverAnchor({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
diff --git a/mobile-magic/apps/frontend/package.json b/mobile-magic/apps/frontend/package.json
index 4025e6f..868a545 100644
--- a/mobile-magic/apps/frontend/package.json
+++ b/mobile-magic/apps/frontend/package.json
@@ -15,6 +15,7 @@
"@radix-ui/react-avatar": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-label": "^2.1.2",
+ "@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3",
diff --git a/mobile-magic/apps/primary-backend/.env.example b/mobile-magic/apps/primary-backend/.env.example
index f1c1ade..57cdee2 100644
--- a/mobile-magic/apps/primary-backend/.env.example
+++ b/mobile-magic/apps/primary-backend/.env.example
@@ -1 +1,3 @@
-JWT_PUBLIC_KEY=
\ No newline at end of file
+JWT_PUBLIC_KEY=
+GITHUB_CLIENT_ID=
+GITHUB_CLIENT_SECRET=
\ No newline at end of file
diff --git a/mobile-magic/apps/primary-backend/index.ts b/mobile-magic/apps/primary-backend/index.ts
index 554ac18..5b0eeba 100644
--- a/mobile-magic/apps/primary-backend/index.ts
+++ b/mobile-magic/apps/primary-backend/index.ts
@@ -2,6 +2,7 @@ import { prismaClient } from "db/client";
import express from "express";
import cors from "cors";
import { authMiddleware } from "common/middleware";
+import axios from "axios";
const app = express();
@@ -40,6 +41,107 @@ app.get("/prompts/:projectId", authMiddleware, async (req, res) => {
res.json({ prompts });
});
+app.get("/github/repoUrl", authMiddleware, async (req, res) => {
+ const { projectId } = req.query;
+ const userId = req.userId!;
+ if (!projectId) {
+ res.status(400).json({ error: "ProjectId does not exist" });
+ return;
+ }
+ const project = await prismaClient.project.findFirst({
+ where: {
+ id: projectId as string,
+ userId,
+ },
+ });
+ if (!project) {
+ res.status(400).json({ error: "Project does not exist" });
+ return;
+ }
+ res.json({ repoUrl: project.repoUrl });
+});
+
+app.get("/github/username", authMiddleware, async (req, res) => {
+ const userId = req.userId!;
+ const gitHub = await prismaClient.gitHubUser.findFirst({
+ where: {
+ userId,
+ },
+ });
+ if (!gitHub) res.json({ isConnected: false, gitHubUsername: null });
+ else res.json({ isConnected: true, gitHubUsername: gitHub.username });
+});
+
+app.post("/github/token", authMiddleware, async (req, res) => {
+ const { code, redirectUri } = req.body;
+ const userId = req.userId!;
+ try {
+ const tokenRes = await axios.post(
+ "https://github.com/login/oauth/access_token",
+ {
+ client_id: process.env.GITHUB_CLIENT_ID,
+ client_secret: process.env.GITHUB_CLIENT_SECRET,
+ code,
+ redirect_uri: redirectUri,
+ },
+ {
+ headers: { Accept: "application/json" },
+ }
+ );
+ if (tokenRes.status != 200) {
+ console.log("OAuth error");
+ res.status(500).send("GitHub OAuth failed.");
+ return;
+ }
+ const accessToken = tokenRes.data.access_token;
+ const userRes = await axios.get("https://api.github.com/user", {
+ headers: { Authorization: `Bearer ${accessToken}` },
+ });
+ if (userRes.status != 200) {
+ console.log("Error fetching user details");
+ res.status(500).send("Error fetching user details");
+ return;
+ }
+ const username = userRes.data.login;
+ await prismaClient.gitHubUser.create({
+ data: {
+ userId,
+ accessToken,
+ username,
+ },
+ });
+ res.json({ username });
+ } catch (err) {
+ console.log("OAuth error:", err);
+ res.status(500).send("GitHub OAuth failed.");
+ }
+});
+
+app.post("/github/createRepo", authMiddleware, async (req, res) => {
+ const { projectId, workerUrl } = req.body;
+ const userId = req.userId!;
+ const github = await prismaClient.gitHubUser.findFirst({
+ where: { userId }
+ });
+ if(!github) {
+ res.status(400).send("Access Token does not exist");
+ return;
+ }
+ try {
+ const response = await axios.post(`${workerUrl}/github/createRepo`, {
+ projectId
+ });
+ if(response.status != 200) {
+ res.status(500).send("Error while creating a new repo (or) while pushing the code to GitHub");
+ return;
+ }
+ res.json(response.data);
+ } catch (error) {
+ res.status(500).send("Error while creating a new repo (or) while pushing the code to GitHub");
+ return;
+ }
+});
+
app.listen(9090, () => {
console.log("Server is running on port 9090");
});
\ No newline at end of file
diff --git a/mobile-magic/apps/primary-backend/package.json b/mobile-magic/apps/primary-backend/package.json
index eb8c389..32cd01a 100644
--- a/mobile-magic/apps/primary-backend/package.json
+++ b/mobile-magic/apps/primary-backend/package.json
@@ -13,6 +13,7 @@
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@types/jsonwebtoken": "^9.0.9",
+ "axios": "^1.8.3",
"cors": "^2.8.5",
"express": "^4.21.2",
"jsonwebtoken": "^9.0.2",
diff --git a/mobile-magic/apps/worker/.env.example b/mobile-magic/apps/worker/.env.example
index 2be99c8..91e8815 100644
--- a/mobile-magic/apps/worker/.env.example
+++ b/mobile-magic/apps/worker/.env.example
@@ -1,2 +1,3 @@
ANTHROPIC_API_KEY=your_anthropic_api_key
-JWT_PUBLIC_KEY=your_jwt_public_key
\ No newline at end of file
+JWT_PUBLIC_KEY=your_jwt_public_key
+WS_RELAYER_URL=
\ No newline at end of file
diff --git a/mobile-magic/apps/worker/index.ts b/mobile-magic/apps/worker/index.ts
index e0c7967..cfff067 100644
--- a/mobile-magic/apps/worker/index.ts
+++ b/mobile-magic/apps/worker/index.ts
@@ -2,18 +2,19 @@ import cors from "cors";
import express from "express";
import { prismaClient } from "db/client";
import Anthropic from '@anthropic-ai/sdk';
-import { systemPrompt } from "./systemPrompt";
+import { systemPrompt, systemPromptForRepoName } from "./systemPrompt";
import { ArtifactProcessor } from "./parser";
-import { onFileUpdate, onPromptEnd, onShellCommand } from "./os";
+import { getBaseWorkerDir, onFileUpdate, onPromptEnd, onShellCommand, pushToGitHubFromDockerContainer } from "./os";
import { RelayWebsocket } from "./ws";
+import axios from "axios";
const app = express();
app.use(cors());
app.use(express.json());
+const client = new Anthropic();
app.post("/prompt", async (req, res) => {
const { prompt, projectId } = req.body;
- const client = new Anthropic();
const project = await prismaClient.project.findUnique({
where: {
id: projectId,
@@ -98,7 +99,135 @@ app.post("/prompt", async (req, res) => {
console.log("error", error);
});
- res.json({ response });
+ // Wait some time till all the files get updated and then push to GitHub
+ await new Promise((r) => setTimeout(r, 1000 * 30));
+
+ let isPushToGitHubSuccess: {
+ isSuccess: boolean;
+ message: string;
+ };
+ if(!project.repoUrl) {
+ isPushToGitHubSuccess = {
+ isSuccess: false,
+ message: "Project is not connected to GitHub"
+ };
+ } else {
+ const github = await prismaClient.gitHubUser.findFirst({
+ where: {
+ userId: project.userId
+ }
+ });
+ if(!github)
+ isPushToGitHubSuccess = {
+ isSuccess: false,
+ message: "Access Token does not exist"
+ }
+ else {
+ try {
+ const userRes = await axios.get("https://api.github.com/user", {
+ headers: { Authorization: `Bearer ${github.accessToken}` },
+ });
+ if (userRes.status == 401 || userRes.status == 403) {
+ console.log("Access token is not valid");
+ isPushToGitHubSuccess = {
+ isSuccess: false,
+ message: "Access token is not valid"
+ };
+ } else {
+ // For the 3rd parameter in the below function, we should give the complete path of this particular project directory (which is in code-server docker container). For now, given /tmp/project.type for the path.
+ const isSuccess = await pushToGitHubFromDockerContainer(project.repoUrl, github.accessToken, getBaseWorkerDir(project.type), "Added changes", false);
+ isPushToGitHubSuccess = {
+ isSuccess,
+ message: isSuccess ? "Successfully pushed to GitHub" : "Error while pushing to GitHub"
+ }
+ }
+ } catch (error) {
+ console.log(error);
+ isPushToGitHubSuccess = {
+ isSuccess: false,
+ message: "Error while checking if access token is valid or not"
+ };
+ }
+ }
+ }
+ res.json({ response, isPushToGitHubSuccess });
+});
+
+app.post("/github/createRepo", async (req, res) => {
+ const { projectId } = req.body;
+ const project = await prismaClient.project.findFirst({
+ where: {
+ id: projectId
+ }
+ });
+ if(!project) {
+ res.status(400).json({ error: "Project does not exist" });
+ return;
+ }
+ const github = await prismaClient.gitHubUser.findFirst({
+ where: {
+ userId: project.userId
+ }
+ });
+ if(!github) {
+ res.status(400).json({ error: "Access token does not exist" });
+ return;
+ }
+ const initialPrompt = await prismaClient.prompt.findFirst({
+ where: {
+ projectId
+ },
+ orderBy: {
+ createdAt: "asc"
+ },
+ take: 1
+ });
+ if(!initialPrompt) {
+ res.status(400).json({ error: "Initial Prompt does not exist" });
+ return;
+ }
+ const msg = await client.messages.create({
+ model: "claude-3-7-sonnet-20250219",
+ max_tokens: 1024,
+ system: systemPromptForRepoName(),
+ messages: [{ role: "user", content: initialPrompt.content }],
+ });
+ const repoName = msg.content[0].type == "text" && msg.content[0].text || ("repo-" + Math.random().toString());
+ try {
+ const response = await axios.post(
+ "https://api.github.com/user/repos",
+ { name: repoName, private: false },
+ {
+ headers: {
+ Authorization: `Bearer ${github.accessToken}`,
+ Accept: "application/vnd.github+json"
+ }
+ }
+ );
+ if(response.status != 201) {
+ res.status(400).json({ error: "Error while creating a new repo" });
+ return;
+ }
+ // For the 3rd parameter in the below function, we should give the complete path of this particular project directory (which is in code-server docker container). For now, given /tmp/project.type for the path.
+ const isPushSuccess = await pushToGitHubFromDockerContainer(response.data.html_url, github.accessToken, getBaseWorkerDir(project.type), "Initial Commit", true);
+ if(isPushSuccess) {
+ await prismaClient.project.update({
+ where: {
+ id: projectId
+ },
+ data: {
+ repoUrl: response.data.html_url
+ }
+ });
+ res.json({ repoUrl: response.data.html_url });
+ }
+ else
+ res.status(500).json({ error: "Error pushing code to GitHub" });
+ } catch (error) {
+ console.log(error);
+ res.status(500).json({ error: "Error while creating a new repo" });
+ return;
+ }
});
app.listen(9091, () => {
diff --git a/mobile-magic/apps/worker/os.ts b/mobile-magic/apps/worker/os.ts
index d35003b..2003c47 100644
--- a/mobile-magic/apps/worker/os.ts
+++ b/mobile-magic/apps/worker/os.ts
@@ -1,15 +1,20 @@
import { prismaClient } from "db/client";
import { RelayWebsocket } from "./ws";
+import { exec } from "child_process";
-function getBaseWorkerDir(type: "NEXTJS" | "REACT_NATIVE") {
+const DOCKER_CONTAINER_NAME_RUNNING_CODE_SERVER = "my-code-server";
+
+export function getBaseWorkerDir(type: "NEXTJS" | "REACT_NATIVE" | "REACT") {
if (type === "NEXTJS") {
return "/tmp/next-app";
- }
- return "/tmp/mobile-app";
+ } else if(type === "REACT")
+ return "/tmp/react-app"
+ else
+ return "/tmp/mobile-app";
}
-export async function onFileUpdate(filePath: string, fileContent: string, projectId: string, promptId: string, type: "NEXTJS" | "REACT_NATIVE") {
+export async function onFileUpdate(filePath: string, fileContent: string, projectId: string, promptId: string, type: "NEXTJS" | "REACT_NATIVE" | "REACT") {
await prismaClient.action.create({
data: {
projectId,
@@ -34,7 +39,7 @@ export async function onShellCommand(shellCommand: string, projectId: string, pr
for (const command of commands) {
console.log(`Running command: ${command}`);
- ws.send(JSON.stringify({
+ RelayWebsocket.getInstance().send(JSON.stringify({
event: "admin",
data: {
type: "command",
@@ -54,10 +59,42 @@ export async function onShellCommand(shellCommand: string, projectId: string, pr
export function onPromptEnd(promptId: string) {
- ws.send(JSON.stringify({
+ RelayWebsocket.getInstance().send(JSON.stringify({
event: "admin",
data: {
type: "prompt-end"
}
}))
+}
+
+export function pushToGitHubFromDockerContainer(repoUrl: string, accessToken: string, projectPath: string, commitMessage: string, isInitialCommit: boolean): Promise {
+ const containerName = DOCKER_CONTAINER_NAME_RUNNING_CODE_SERVER;
+ const gitCommands = `
+ cd ${projectPath} && \
+ ${isInitialCommit && `git init && \
+ git remote add origin ${getRemoteOrigin(repoUrl, accessToken)} && \
+ git branch -M main && \ `}
+ git config user.email "mobile-magic@github.com" && \
+ git config user.name "Mobile Magic" && \
+ git add . && \
+ git commit -m "${commitMessage}" && \
+ git push -u origin main
+ `;
+ return new Promise((resolve) => {
+ exec(`docker exec ${containerName} sh -c '${gitCommands}'`, (err, stdout, stderr) => {
+ if (err) {
+ console.log("Error:", err.message);
+ resolve(false);
+ } else {
+ console.log("Git push output: ", stdout);
+ console.log("Stderr: ", stderr);
+ resolve(true);
+ }
+ });
+ });
+};
+
+function getRemoteOrigin(repoUrl: string, accessToken: string): string {
+ const userNameRepoName = repoUrl.split("/").slice(-2).join("/");
+ return `https://${accessToken}@github.com/${userNameRepoName}.git`;
}
\ No newline at end of file
diff --git a/mobile-magic/apps/worker/package.json b/mobile-magic/apps/worker/package.json
index 8012117..1f7b13e 100644
--- a/mobile-magic/apps/worker/package.json
+++ b/mobile-magic/apps/worker/package.json
@@ -11,6 +11,7 @@
"dependencies": {
"@anthropic-ai/sdk": "^0.38.0",
"@types/express": "^5.0.0",
+ "axios": "^1.8.3",
"cors": "^2.8.5",
"express": "^4.21.2",
"common": "*"
diff --git a/mobile-magic/apps/worker/systemPrompt.tsx b/mobile-magic/apps/worker/systemPrompt.tsx
index 27f3a68..69b2af0 100644
--- a/mobile-magic/apps/worker/systemPrompt.tsx
+++ b/mobile-magic/apps/worker/systemPrompt.tsx
@@ -1864,3 +1864,9 @@ ${ARTIFACT_INFO}
${projectType === "NEXTJS" ? NEXT_JS_ARTIFACT_INFO : projectType === "REACT_NATIVE" ? REACT_NATIVE_ARTIFACT_INFO : REACT_ARTIFACT_INFO}
`;
+
+export const systemPromptForRepoName = () => `Given prompt will be a React Application (or) React Native Application (or) Next.js Application.
+Depending on the prompt, give a good, meaningful and suitable "GitHub Repository Name" for that application. ** Only just give the repository name as the output **
+Example:
+User Prompt: Create a todo application.
+Response: Todo App`;
\ No newline at end of file
diff --git a/mobile-magic/apps/worker/ws.ts b/mobile-magic/apps/worker/ws.ts
index be5ca02..2539d81 100644
--- a/mobile-magic/apps/worker/ws.ts
+++ b/mobile-magic/apps/worker/ws.ts
@@ -4,22 +4,26 @@ export class RelayWebsocket {
private static instance: RelayWebsocket;
private ws: WebSocket;
private callbacks: Map void>;
+ private isOpen: boolean = false;
+ private bufferedMessages: any[] = [];
private constructor(url: string) {
this.ws = new WebSocket(url);
this.callbacks = new Map();
- this.ws.onmessage = (event) => {
- const { callbackId, ...data } = JSON.parse(event.data);
+ this.ws.onmessage = (eventMessage) => {
+ const { callbackId, diff, event } = JSON.parse(eventMessage.data);
const callback = this.callbacks.get(callbackId);
if (callback) {
- callback(data);
+ callback({ diff });
}
};
this.ws.onopen = () => {
+ this.isOpen = true;
this.send(JSON.stringify({
event: "api_subscribe",
}));
+ this.bufferedMessages.forEach((m) => this.send(m));
}
}
@@ -31,11 +35,15 @@ export class RelayWebsocket {
}
send(message: string) {
+ if(!this.isOpen) {
+ this.bufferedMessages.push(message);
+ return;
+ }
this.ws.send(message);
}
sendAndAwaitResponse(message: any, callbackId: string): Promise {
- this.ws.send(JSON.stringify({...message, callbackId}));
+ this.send(JSON.stringify({...message, callbackId}));
return new Promise((resolve, reject) => {
this.callbacks.set(callbackId, resolve);
diff --git a/mobile-magic/apps/ws-relayer/index.ts b/mobile-magic/apps/ws-relayer/index.ts
index b219a55..3c62524 100644
--- a/mobile-magic/apps/ws-relayer/index.ts
+++ b/mobile-magic/apps/ws-relayer/index.ts
@@ -17,7 +17,7 @@ Bun.serve({
return new Response("Upgrade failed", { status: 500 });
},
websocket: {
- message(ws, message) {
+ message(ws: any, message: any) {
const { event, data }: MessagePayload = JSON.parse(message.toString());
if (event === "subscribe") {
SUBSCRIPTIONS.push(ws);
@@ -33,14 +33,14 @@ Bun.serve({
}
} else if (event === "api_subscribe") {
API_SUBSCRIPTIONS.push(ws);
- } else if (event === "vscode") {
+ } else if (event === "vscode_diff") {
API_SUBSCRIPTIONS.forEach(ws => ws.send(JSON.stringify(data)));
}
},
- open(ws) {
+ open(ws: any) {
console.log("open");
},
- close(ws) {
+ close(ws: any) {
console.log("close");
},
diff --git a/mobile-magic/bun.lock b/mobile-magic/bun.lock
index ccb1fdc..3c2ccb0 100644
--- a/mobile-magic/bun.lock
+++ b/mobile-magic/bun.lock
@@ -19,6 +19,7 @@
"@radix-ui/react-avatar": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-label": "^2.1.2",
+ "@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3",
@@ -74,6 +75,7 @@
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@types/jsonwebtoken": "^9.0.9",
+ "axios": "^1.8.3",
"common": "*",
"cors": "^2.8.5",
"express": "^4.21.2",
@@ -92,6 +94,8 @@
"dependencies": {
"@anthropic-ai/sdk": "^0.38.0",
"@types/express": "^5.0.0",
+ "axios": "^1.8.3",
+ "common": "*",
"cors": "^2.8.5",
"express": "^4.21.2",
},
@@ -122,6 +126,7 @@
"name": "ws-relayer",
"devDependencies": {
"@types/bun": "latest",
+ "common": "*",
},
"peerDependencies": {
"typescript": "^5.0.0",
@@ -490,6 +495,8 @@
"@radix-ui/react-label": ["@radix-ui/react-label@2.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw=="],
+ "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg=="],
+
"@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.2", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-rect": "1.1.0", "@radix-ui/react-use-size": "1.1.0", "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA=="],
"@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="],
@@ -814,7 +821,7 @@
"axe-core": ["axe-core@4.10.3", "", {}, "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg=="],
- "axios": ["axios@1.8.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg=="],
+ "axios": ["axios@1.8.3", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
@@ -1580,7 +1587,7 @@
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
- "redis": ["redis@workspace:packages/redis"],
+ "redis": ["redis@4.7.0", "", { "dependencies": { "@redis/bloom": "1.2.0", "@redis/client": "1.6.0", "@redis/graph": "1.1.1", "@redis/json": "1.0.7", "@redis/search": "1.2.0", "@redis/time-series": "1.1.0" } }, "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ=="],
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
@@ -2100,7 +2107,7 @@
"jsonwebtoken/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "k8s-orchestrator/redis": ["redis@4.7.0", "", { "dependencies": { "@redis/bloom": "1.2.0", "@redis/client": "1.6.0", "@redis/graph": "1.1.1", "@redis/json": "1.0.7", "@redis/search": "1.2.0", "@redis/time-series": "1.1.0" } }, "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ=="],
+ "k8s-orchestrator/@types/bun": ["@types/bun@1.2.5", "", { "dependencies": { "bun-types": "1.2.5" } }, "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg=="],
"log-symbols/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
@@ -2124,6 +2131,8 @@
"postcss/picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+ "primary-backend/@types/bun": ["@types/bun@1.2.5", "", { "dependencies": { "bun-types": "1.2.5" } }, "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg=="],
+
"rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
"react-remove-scroll/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
@@ -2132,8 +2141,6 @@
"react-style-singleton/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
- "redis/redis": ["redis@4.7.0", "", { "dependencies": { "@redis/bloom": "1.2.0", "@redis/client": "1.6.0", "@redis/graph": "1.1.1", "@redis/json": "1.0.7", "@redis/search": "1.2.0", "@redis/time-series": "1.1.0" } }, "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ=="],
-
"rxjs/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
@@ -2160,6 +2167,10 @@
"use-sidecar/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+ "worker/@types/bun": ["@types/bun@1.2.5", "", { "dependencies": { "bun-types": "1.2.5" } }, "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg=="],
+
+ "ws-relayer/@types/bun": ["@types/bun@1.2.5", "", { "dependencies": { "bun-types": "1.2.5" } }, "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg=="],
+
"@anthropic-ai/sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
@@ -2190,6 +2201,8 @@
"inquirer/ora/log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="],
+ "k8s-orchestrator/@types/bun/bun-types": ["bun-types@1.2.5", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-3oO6LVGGRRKI4kHINx5PIdIgnLRb7l/SprhzqXapmoYkFl5m4j6EvALvbDVuuBFaamB46Ap6HCUxIXNLCGy+tg=="],
+
"log-symbols/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
"log-symbols/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
@@ -2202,16 +2215,24 @@
"node-plop/inquirer/rxjs": ["rxjs@6.6.7", "", { "dependencies": { "tslib": "^1.9.0" } }, "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ=="],
+ "primary-backend/@types/bun/bun-types": ["bun-types@1.2.5", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-3oO6LVGGRRKI4kHINx5PIdIgnLRb7l/SprhzqXapmoYkFl5m4j6EvALvbDVuuBFaamB46Ap6HCUxIXNLCGy+tg=="],
+
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"snakecase-keys/snake-case/dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="],
"snakecase-keys/snake-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+ "worker/@types/bun/bun-types": ["bun-types@1.2.5", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-3oO6LVGGRRKI4kHINx5PIdIgnLRb7l/SprhzqXapmoYkFl5m4j6EvALvbDVuuBFaamB46Ap6HCUxIXNLCGy+tg=="],
+
+ "ws-relayer/@types/bun/bun-types": ["bun-types@1.2.5", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-3oO6LVGGRRKI4kHINx5PIdIgnLRb7l/SprhzqXapmoYkFl5m4j6EvALvbDVuuBFaamB46Ap6HCUxIXNLCGy+tg=="],
+
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
+ "k8s-orchestrator/@types/bun/bun-types/@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
+
"log-symbols/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"log-symbols/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
@@ -2220,8 +2241,14 @@
"node-plop/inquirer/rxjs/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
+ "primary-backend/@types/bun/bun-types/@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
+
"snakecase-keys/snake-case/dot-case/no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="],
+ "worker/@types/bun/bun-types/@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
+
+ "ws-relayer/@types/bun/bun-types/@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
+
"log-symbols/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
"minizlib/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
diff --git a/mobile-magic/packages/common/types.ts b/mobile-magic/packages/common/types.ts
index b6cfb91..6dbfa2d 100644
--- a/mobile-magic/packages/common/types.ts
+++ b/mobile-magic/packages/common/types.ts
@@ -6,14 +6,23 @@ export type MessagePayload = {
event: "admin";
data: {
type: "command" | "update-file" | "prompt-start" | "prompt-end"
- content: string;
+ content?: string;
path?: string;
};
callbackId?: string;
+} | {
+ event: "vscode_diff";
+ data: {
+ diff: string;
+ callbackId: string;
+ }
+} | {
+ event: "api_subscribe";
+ data?: null;
}
export type VscodeMessagePayload = {
- event: "vscode_diff";
+ // event: "vscode_diff";
diff: string;
- callbackId: string;
+ // callbackId: string;
}
\ No newline at end of file
diff --git a/mobile-magic/packages/db/prisma/migrations/20250312205138_added_model_git_hub_user_and_repo_url_field_in_project_model/migration.sql b/mobile-magic/packages/db/prisma/migrations/20250312205138_added_model_git_hub_user_and_repo_url_field_in_project_model/migration.sql
new file mode 100644
index 0000000..e4348c0
--- /dev/null
+++ b/mobile-magic/packages/db/prisma/migrations/20250312205138_added_model_git_hub_user_and_repo_url_field_in_project_model/migration.sql
@@ -0,0 +1,18 @@
+-- AlterTable
+ALTER TABLE "Project" ADD COLUMN "repoUrl" TEXT;
+
+-- CreateTable
+CREATE TABLE "GitHubUser" (
+ "id" TEXT NOT NULL,
+ "userId" TEXT NOT NULL,
+ "accessToken" TEXT NOT NULL,
+ "username" TEXT NOT NULL,
+
+ CONSTRAINT "GitHubUser_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateIndex
+CREATE UNIQUE INDEX "GitHubUser_userId_key" ON "GitHubUser"("userId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "GitHubUser_accessToken_key" ON "GitHubUser"("accessToken");
diff --git a/mobile-magic/packages/db/prisma/schema.prisma b/mobile-magic/packages/db/prisma/schema.prisma
index c8f151b..6440607 100644
--- a/mobile-magic/packages/db/prisma/schema.prisma
+++ b/mobile-magic/packages/db/prisma/schema.prisma
@@ -29,6 +29,7 @@ model Project {
actions Action[]
type ProjectType @default(NEXTJS)
userId String
+ repoUrl String?
}
enum ProjectType {
@@ -59,6 +60,13 @@ model Action {
prompt Prompt @relation(fields: [promptId], references: [id])
}
+model GitHubUser {
+ id String @id @default(uuid())
+ userId String @unique
+ accessToken String @unique
+ username String
+}
+
enum PromptType {
USER
SYSTEM