Skip to content

Commit 487a748

Browse files
committed
feat: implement terminal routing for backend mode
- Add terminalType parameter to run-app and restart-app handlers - Route app:output events based on terminalType (frontend/backend) - Update backend restart buttons to use backend terminal routing - Enhance useRunApp hook to support terminalType options This fixes the issue where backend terminal output was not visible in dedicated backend terminal panel, instead getting routed to system messages.
1 parent aeb7588 commit 487a748

File tree

5 files changed

+109
-49
lines changed

5 files changed

+109
-49
lines changed

README.md

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
1-
# 🧠 AliFullStack — AI-Powered Full-Stack App Builder
1+
# 🧠 AliFullStack — AI-Powered Full-Stack App Builder (Local-First, Open Source)
22

3-
**AliFullStack** is a local-first, AI-driven full-stack app builder that empowers you to build, test, and deploy applications with speed, privacy, and full control. Inspired by tools like Lovable, v0, and Bolt, AliFullStack runs entirely in your browser or on your machine — no vendor lock-in, no data leakage.
3+
**AliFullStack** is a blazing-fast, **AI-driven Full-Stack App Builder** that runs entirely **locally** — giving you total **data privacy**, **vendor freedom**, and powerful **LLM integration**. Whether you're a solo indie hacker, startup, or enterprise developer, AliFullStack helps you **build, test, and deploy apps autonomously** using your favorite frontend and backend frameworks.
44

5-
Built on top of [Dyad](https://www.alifullstack.alitech.io), it brings together the power of modern AI models with the flexibility of your favorite frontend and backend frameworks.
5+
> ✅ 100% Local | 🚀 LLM-Powered | 🧠 BYO AI API Key | 🧩 Framework-Agnostic | 💡 Open Source
66
7-
[![AliFullStack Screenshot](https://github.com/user-attachments/assets/f6c83dfc-6ffd-4d32-93dd-4b9c46d17790)](http://alifullstack.alitech.io)
7+
Inspired by tools like *Lovable*, *v0.dev*, and *Bolt*, but with no lock-in or cloud dependency. Built on top of [Dyad](https://github.com/dyad-sh/dyad), AliFullStack gives you the power of AI without sacrificing control.
88

9-
🔗 **Learn more**: [alifullstack.alitech.io](http://alifullstack.alitech.io)
9+
![AliFullStack Screenshot](https://github.com/user-attachments/assets/f6c83dfc-6ffd-4d32-93dd-4b9c46d17790)
10+
11+
🌐 **Live Demo & Docs**: [alifullstack.alitech.io](https://alifullstack.alitech.io)
12+
13+
---
14+
15+
## ⭐ Why Star This Project?
16+
17+
Help us grow! If you're excited about AI developer tools, autonomous coding, or local-first privacy-first software:
18+
19+
👉 **[Give us a ⭐ on GitHub](https://github.com/your-repo-link-here)** — it really helps!
1020

1121
---
1222

1323
## 🚀 Key Features
1424

15-
- **Local & Private**Everything runs locally. No data ever leaves your machine unless you choose.
16-
- 🔑 **Bring Your Own API Keys**Use your preferred AI providers (OpenAI, Claude, Gemini, etc.).
17-
- 🖥️ **Cross-Platform** — Works seamlessly on macOS, Windows, and Linux.
18-
- 🧱 **Framework-Agnostic** Build with React, Next.js, Django, FastAPI, and more.
19-
- 🛠 **Autonomous AI Code Generation**Let AI handle scaffolding, APIs, UI, and even tests.
20-
- 🧠 **Model Flexibility**Plug into a wide range of LLM providers via API.
25+
- 🔐 **Local & Private by Default** — No servers, no tracking, no data leakage.
26+
- 🧠 **LLM-Powered Autonomy** — AI generates frontend, backend, tests, and more.
27+
- 🧰 **Bring Your Own API Keys** — Works with OpenAI, Claude, Gemini, etc.
28+
- 🧱 **Framework Agnostic** — React, Next.js, Django, FastAPI, and more.
29+
- 💻 **Cross-Platform**Windows, macOS, Linux supported.
30+
- 🔄 **Self-Upgrading Pipelines**Code improves itself over time.
2131

2232
---
2333

@@ -83,34 +93,60 @@ AliFullStack is evolving rapidly. Here's what’s done and what’s coming soon:
8393

8494
---
8595

86-
## 📦 Get Started
96+
## 📦 Get Started in 2 Minutes
8797

88-
No sign-up. No cloud lock-in. Just download and start building.
98+
No sign-up. No cloud lock-in. Just download and build.
8999

90-
👉 **[Download for your platform](https://www.alifullstack.alitech.io/#download)**
100+
👉 **[Download AliFullStack](https://www.alifullstack.alitech.io/#download)** for your OS and start building.
91101

92102
---
93103

94-
## 🌍 Community
104+
## 👥 Join the Community
95105

96-
Join the growing community of indie hackers, developers, and AI tinkerers:
106+
Be part of a growing network of **AI tinkerers**, **indie hackers**, and **full-stack dreamers**:
97107

98-
🔗 **[r/alifullstackbuilders](https://www.reddit.com/r/alifullstackbuilders/)** — Share your builds, get support, and contribute ideas.
108+
- 🧵 Reddit: [r/alifullstackbuilders](https://www.reddit.com/r/alifullstackbuilders/)
109+
- 🐦 Twitter/X: [@alifullstack](https://twitter.com/alifullstack) *(coming soon)*
110+
- 🌐 Website: [alifullstack.alitech.io](https://alifullstack.alitech.io)
99111

100112
---
101113

102-
## 🤝 Contributing
114+
## 🤝 Contribute to AliFullStack
103115

104-
AliFullStack is open-source and licensed under **Apache 2.0**.
116+
AliFullStack is **Apache 2.0 licensed** and open to contributors!
105117

106-
We welcome contributions! Whether it's fixing bugs, adding features, or writing docs — you're welcome here. Please check out our [CONTRIBUTING.md](./CONTRIBUTING.md) to get started.
118+
You can:
119+
- 🐛 Fix bugs
120+
- ✨ Add features
121+
- 📝 Improve documentation
122+
- 📣 Share it in dev communities!
123+
124+
📄 **[CONTRIBUTING.md](./CONTRIBUTING.md)** has all the details.
107125

108126
---
109127

110-
## 📬 Stay in the Loop
128+
## 📬 Stay Updated
111129

112-
Get product updates, feature drops, and community highlights:
130+
Be the first to know about:
131+
- ✨ New Features
132+
- 📦 Releases
133+
- 🧠 Devlogs
134+
- 🚀 Launches
113135

114-
- 🌐 Website: [alifullstack.alitech.io](https://alifullstack.alitech.io)
115-
- 🐦 Twitter/X: [@alifullstack](https://twitter.com/alifullstack) *(coming soon)*
116-
- 📢 Reddit: [r/alifullstackbuilders](http)
136+
📧 Subscribe via the [website](https://alifullstack.alitech.io) or follow us on [Reddit](https://www.reddit.com/r/alifullstackbuilders/).
137+
138+
---
139+
140+
## 🔗 Useful Links
141+
142+
- 💻 [Live Demo / Docs]- Coming Soon
143+
- 📥 [Download] - Coming Soon
144+
- 🧑‍💻 [Contribute](./CONTRIBUTING.md)
145+
-[Star on GitHub](https://github.com/SFARPak/AliFullStack)
146+
- 🧵 [Reddit Community](https://www.reddit.com/r/alifullstackbuilders/)
147+
148+
---
149+
150+
## 💬 Let’s Build the Future of Devtools — Together.
151+
152+
> Local-first. AI-powered. Fully yours.

src/components/backend-chat/BackendChatInput.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ function BackendRestartButton() {
701701
if (!selectedAppId) return;
702702

703703
posthog.capture("backend:action:restart");
704-
await restartApp();
704+
await restartApp({}, { terminalType: "backend" });
705705
}, [selectedAppId, posthog, restartApp]);
706706

707707
return (
@@ -873,4 +873,4 @@ function BackendSqlQueryItem({ query }: { query: SqlQuery }) {
873873
)}
874874
</li>
875875
);
876-
}
876+
}

src/hooks/useRunApp.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,12 @@ export function useRunApp() {
139139
}, [setPreviewPanelKey]);
140140

141141
const restartApp = useCallback(
142-
async ({
143-
removeNodeModules = false,
144-
}: { removeNodeModules?: boolean } = {}) => {
142+
async (
143+
params: { removeNodeModules?: boolean } = {},
144+
options: { terminalType?: "frontend" | "backend" | "main" } = {}
145+
) => {
146+
const { removeNodeModules = false } = params;
147+
const { terminalType = "main" } = options;
145148
if (appId === null) {
146149
return;
147150
}

src/ipc/handlers/app_handlers.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,15 @@ async function executeApp({
9292
isNeon,
9393
installCommand,
9494
startCommand,
95+
terminalType,
9596
}: {
9697
appPath: string;
9798
appId: number;
9899
event: Electron.IpcMainInvokeEvent;
99100
isNeon: boolean;
100101
installCommand?: string | null;
101102
startCommand?: string | null;
103+
terminalType?: "frontend" | "backend" | "main";
102104
}): Promise<void> {
103105
if (proxyWorker) {
104106
proxyWorker.terminate();
@@ -208,11 +210,13 @@ function listenToProcess({
208210
appId,
209211
isNeon,
210212
event,
213+
terminalType,
211214
}: {
212215
process: ChildProcess;
213216
appId: number;
214217
isNeon: boolean;
215218
event: Electron.IpcMainInvokeEvent;
219+
terminalType?: "frontend" | "backend" | "main";
216220
}) {
217221
// Log output
218222
spawnedProcess.stdout?.on("data", async (data) => {
@@ -1056,7 +1060,7 @@ export function registerAppHandlers() {
10561060
"run-app",
10571061
async (
10581062
event: Electron.IpcMainInvokeEvent,
1059-
{ appId }: { appId: number },
1063+
{ appId, terminalType }: { appId: number; terminalType?: "frontend" | "backend" | "main" },
10601064
): Promise<void> => {
10611065
return withLock(appId, async () => {
10621066
// Check if app is already running
@@ -1079,14 +1083,15 @@ export function registerAppHandlers() {
10791083
try {
10801084
// There may have been a previous run that left a process on port 32100.
10811085
await cleanUpPort(32100);
1082-
await executeApp({
1083-
appPath,
1084-
appId,
1085-
event,
1086-
isNeon: !!app.neonProjectId,
1087-
installCommand: app.installCommand,
1088-
startCommand: app.startCommand,
1089-
});
1086+
await executeApp({
1087+
appPath,
1088+
appId,
1089+
event,
1090+
isNeon: !!app.neonProjectId,
1091+
installCommand: app.installCommand,
1092+
startCommand: app.startCommand,
1093+
terminalType,
1094+
});
10901095

10911096
return;
10921097
} catch (error: any) {
@@ -1161,7 +1166,8 @@ export function registerAppHandlers() {
11611166
{
11621167
appId,
11631168
removeNodeModules,
1164-
}: { appId: number; removeNodeModules?: boolean },
1169+
terminalType,
1170+
}: { appId: number; removeNodeModules?: boolean; terminalType?: "frontend" | "backend" | "main" },
11651171
): Promise<void> => {
11661172
logger.log(`Restarting app ${appId}`);
11671173
return withLock(appId, async () => {

src/ipc/ipc_client.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export interface ChatStreamCallbacks {
8080
}
8181

8282
export interface AppStreamCallbacks {
83-
onOutput: (output: AppOutput) => void;
83+
onOutput: (output: AppOutput, terminalType?: "frontend" | "backend" | "main") => void;
8484
}
8585

8686
export interface GitHubDeviceFlowUpdateData {
@@ -151,18 +151,30 @@ export class IpcClient {
151151
}
152152
});
153153

154-
this.ipcRenderer.on("app:output", (data) => {
154+
this.ipcRenderer.on("app:output", async (data) => {
155155
if (
156156
data &&
157157
typeof data === "object" &&
158158
"type" in data &&
159159
"message" in data &&
160160
"appId" in data
161161
) {
162-
const { type, message, appId } = data as unknown as AppOutput;
163-
const callbacks = this.appStreams.get(appId);
164-
if (callbacks) {
165-
callbacks.onOutput({ type, message, appId, timestamp: Date.now() });
162+
const { type, message, appId, terminalType } = data as unknown as AppOutput & { terminalType?: "frontend" | "backend" | "main" };
163+
164+
// Route based on terminalType if provided
165+
if (terminalType && terminalType !== "main") {
166+
try {
167+
const { addTerminalOutput } = await import("./handlers/terminal_handlers");
168+
addTerminalOutput(appId, terminalType, message, type as "command" | "output" | "success" | "error");
169+
} catch (error) {
170+
showError(new Error(`[IPC] Failed to route terminal output: ${error}`));
171+
}
172+
} else {
173+
// Default routing for main/system messages
174+
const callbacks = this.appStreams.get(appId);
175+
if (callbacks) {
176+
callbacks.onOutput({ type, message, appId, timestamp: Date.now() }, terminalType);
177+
}
166178
}
167179
} else {
168180
showError(new Error(`[IPC] Invalid app output data received: ${data}`));
@@ -478,9 +490,10 @@ export class IpcClient {
478490
// Run an app
479491
public async runApp(
480492
appId: number,
481-
onOutput: (output: AppOutput) => void,
493+
onOutput: (output: AppOutput, terminalType?: "frontend" | "backend" | "main") => void,
494+
terminalType: "frontend" | "backend" | "main" = "main",
482495
): Promise<void> {
483-
await this.ipcRenderer.invoke("run-app", { appId });
496+
await this.ipcRenderer.invoke("run-app", { appId, terminalType });
484497
this.appStreams.set(appId, { onOutput });
485498
}
486499

@@ -492,13 +505,15 @@ export class IpcClient {
492505
// Restart a running app
493506
public async restartApp(
494507
appId: number,
495-
onOutput: (output: AppOutput) => void,
508+
onOutput: (output: AppOutput, terminalType?: "frontend" | "backend" | "main") => void,
496509
removeNodeModules?: boolean,
510+
terminalType: "frontend" | "backend" | "main" = "main",
497511
): Promise<{ success: boolean }> {
498512
try {
499513
const result = await this.ipcRenderer.invoke("restart-app", {
500514
appId,
501515
removeNodeModules,
516+
terminalType,
502517
});
503518
this.appStreams.set(appId, { onOutput });
504519
return result;

0 commit comments

Comments
 (0)