Skip to content

Commit fce192f

Browse files
committed
fix: critical bugs, security, console.log cleanup, tests, README
- Fix memory leaks in chat_stream_handlers and help_bot_handlers - Remove hardcoded GitHub OAuth client ID - Generate random Django SECRET_KEY instead of hardcoded placeholder - Remove ~40 stray console.log from renderer and main process - Fix unhandled promise rejection in app_handlers auto-install fallback - Replace console.log with electron-log in process_manager, ipc_client, terminal_handlers, debug_handlers, start_proxy_server - Fix key={index} misuse in ChatInput, MessagesList, home, Console, FileTree - Add aria-labels and type=button to icon-only ChatInput buttons - Update README: mark Roo Code Cloud done, add missing integrations - Add unit tests for lib/utils, lib/assert, lib/schemas - Unskip e2e chat_search tests
1 parent 8cd9bc3 commit fce192f

35 files changed

Lines changed: 276 additions & 116 deletions

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,25 @@ AliFullStack is evolving rapidly. Here's what’s done and what’s coming soon:
8181
- [x] [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
8282
- [x] [xAI](https://x.ai)
8383
- [x] [AWS Bedrock](https://aws.amazon.com/bedrock/)
84-
- [ ] [Roo Code Cloud](https://roocode.com)
84+
- [x] [Roo Code Cloud](https://roocode.com)
8585
- [ ] Mistral _(planned)_
8686
- [ ] Cohere _(planned)_
8787

88+
### 🧩 Local & Extended Providers
89+
90+
- [x] [Ollama](https://ollama.ai) (local models)
91+
- [x] [LM Studio](https://lmstudio.ai) (local models)
92+
- [x] [NVIDIA NIM](https://build.nvidia.com) (direct OpenAI-compatible)
93+
- [x] Custom OpenAI-compatible providers
94+
95+
### 🔌 Integrations
96+
97+
- [x] [GitHub](https://github.com) (OAuth + push + repo management)
98+
- [x] [Vercel](https://vercel.com) (deploy + framework detection)
99+
- [x] [Supabase](https://supabase.com) (project management + edge functions)
100+
- [x] [Neon](https://neon.tech) (serverless Postgres)
101+
- [ ] AWS _(planned)_
102+
88103
### ✨ In Progress & Planned Features
89104

90105
- [x] Full-stack project generation
@@ -96,6 +111,7 @@ AliFullStack is evolving rapidly. Here's what’s done and what’s coming soon:
96111
- [x] Self-improving development pipeline
97112
- [x] **Danger Zone Features** — Advanced app management tools
98113
- [x] Delete All Apps functionality
114+
- [x] Reset Everything functionality
99115
- [ ] Drag-and-drop UI builder
100116
- [ ] Advanced code generation patterns
101117
- [ ] AI-assisted test and schema generation

e2e-tests/chat_search.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { test } from "./helpers/test_helper";
22

3-
test.skip("chat search - basic search dialog functionality", async ({ po }) => {
3+
test("chat search - basic search dialog functionality", async ({ po }) => {
44
await po.setUp({ autoApprove: true });
55
await po.importApp("minimal");
66

@@ -41,7 +41,7 @@ test.skip("chat search - basic search dialog functionality", async ({ po }) => {
4141
await po.page.keyboard.press("Escape");
4242
});
4343

44-
test.skip("chat search - with named chats for easier testing", async ({
44+
test("chat search - with named chats for easier testing", async ({
4545
po,
4646
}) => {
4747
await po.setUp({ autoApprove: true });
@@ -85,7 +85,7 @@ test.skip("chat search - with named chats for easier testing", async ({
8585
await po.page.keyboard.press("Escape");
8686
});
8787

88-
test.skip("chat search - keyboard shortcut functionality", async ({ po }) => {
88+
test("chat search - keyboard shortcut functionality", async ({ po }) => {
8989
await po.setUp({ autoApprove: true });
9090
await po.importApp("minimal");
9191

@@ -102,7 +102,7 @@ test.skip("chat search - keyboard shortcut functionality", async ({ po }) => {
102102
await po.page.getByTestId("chat-search-dialog").waitFor({ state: "hidden" });
103103
});
104104

105-
test.skip("chat search - navigation and selection", async ({ po }) => {
105+
test("chat search - navigation and selection", async ({ po }) => {
106106
await po.setUp({ autoApprove: true });
107107
await po.importApp("minimal");
108108

src/__tests__/assert.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { describe, it, expect } from "vitest";
2+
import { assertExists } from "./assert";
3+
4+
describe("assertExists", () => {
5+
it("does not throw for a defined value", () => {
6+
expect(() => assertExists("hello", "should exist")).not.toThrow();
7+
expect(() => assertExists(0, "should exist")).not.toThrow();
8+
expect(() => assertExists(false, "should exist")).not.toThrow();
9+
expect(() => assertExists("", "should exist")).not.toThrow();
10+
});
11+
12+
it("throws for undefined", () => {
13+
expect(() => assertExists(undefined, "missing value")).toThrow(
14+
"missing value",
15+
);
16+
});
17+
18+
it("throws for null", () => {
19+
expect(() => assertExists(null, "missing value")).toThrow("missing value");
20+
});
21+
22+
it("throws with the provided message", () => {
23+
const message = "custom error message";
24+
try {
25+
assertExists(undefined, message);
26+
expect.fail("should have thrown");
27+
} catch (e) {
28+
expect(e).toBeInstanceOf(Error);
29+
expect((e as Error).message).toBe(message);
30+
}
31+
});
32+
});

src/__tests__/schemas.test.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { describe, it, expect } from "vitest";
2+
import { z } from "zod";
3+
import {
4+
isAliFullStackProEnabled,
5+
hasAliFullStackProKey,
6+
ChatSummarySchema,
7+
AppSearchResultSchema,
8+
} from "./schemas";
9+
10+
describe("isAliFullStackProEnabled", () => {
11+
it("returns false when enableAliFullStackPro is false", () => {
12+
const settings = {
13+
enableAliFullStackPro: false,
14+
providerSettings: { auto: { apiKey: { value: "key" } } },
15+
} as any;
16+
expect(isAliFullStackProEnabled(settings)).toBe(false);
17+
});
18+
19+
it("returns false when enableAliFullStackPro is undefined", () => {
20+
const settings = {
21+
providerSettings: { auto: { apiKey: { value: "key" } } },
22+
} as any;
23+
expect(isAliFullStackProEnabled(settings)).toBe(false);
24+
});
25+
26+
it("returns false when apiKey is missing", () => {
27+
const settings = {
28+
enableAliFullStackPro: true,
29+
providerSettings: { auto: { apiKey: {} } },
30+
} as any;
31+
expect(isAliFullStackProEnabled(settings)).toBe(false);
32+
});
33+
34+
it("returns true when enabled and apiKey exists", () => {
35+
const settings = {
36+
enableAliFullStackPro: true,
37+
providerSettings: { auto: { apiKey: { value: "key" } } },
38+
} as any;
39+
expect(isAliFullStackProEnabled(settings)).toBe(true);
40+
});
41+
});
42+
43+
describe("hasAliFullStackProKey", () => {
44+
it("returns true when apiKey value is present", () => {
45+
const settings = {
46+
providerSettings: { auto: { apiKey: { value: "key" } } },
47+
} as any;
48+
expect(hasAliFullStackProKey(settings)).toBe(true);
49+
});
50+
51+
it("returns false when providerSettings is missing", () => {
52+
const settings = {} as any;
53+
expect(hasAliFullStackProKey(settings)).toBe(false);
54+
});
55+
56+
it("returns false when apiKey is empty string", () => {
57+
const settings = {
58+
providerSettings: { auto: { apiKey: { value: "" } } },
59+
} as any;
60+
expect(hasAliFullStackProKey(settings)).toBe(false);
61+
});
62+
});
63+
64+
describe("Zod schemas", () => {
65+
it("ChatSummarySchema parses valid data", () => {
66+
const data = {
67+
id: 1,
68+
appId: 2,
69+
title: "Test Chat",
70+
createdAt: new Date(),
71+
};
72+
expect(() => ChatSummarySchema.parse(data)).not.toThrow();
73+
});
74+
75+
it("ChatSummarySchema rejects missing id", () => {
76+
expect(() =>
77+
ChatSummarySchema.parse({
78+
appId: 2,
79+
title: "Test",
80+
createdAt: new Date(),
81+
}),
82+
).toThrow();
83+
});
84+
85+
it("AppSearchResultSchema parses valid data", () => {
86+
const data = {
87+
id: 1,
88+
name: "My App",
89+
createdAt: new Date(),
90+
matchedChatTitle: null,
91+
matchedChatMessage: null,
92+
};
93+
expect(() => AppSearchResultSchema.parse(data)).not.toThrow();
94+
});
95+
});

src/__tests__/utils.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { describe, it, expect } from "vitest";
2+
import { generateCuteAppName, cn } from "./utils";
3+
4+
describe("generateCuteAppName", () => {
5+
it("returns a non-empty string", () => {
6+
const name = generateCuteAppName();
7+
expect(name).toBeTruthy();
8+
expect(typeof name).toBe("string");
9+
});
10+
11+
it("returns a name with at least two words separated by a space", () => {
12+
const name = generateCuteAppName();
13+
const parts = name.split(" ");
14+
expect(parts.length).toBeGreaterThanOrEqual(2);
15+
});
16+
17+
it("returns different names on successive calls (with high probability)", () => {
18+
const names = new Set<string>();
19+
for (let i = 0; i < 50; i++) {
20+
names.add(generateCuteAppName());
21+
}
22+
// With 50 draws from a large pool, collisions should be rare
23+
expect(names.size).toBeGreaterThan(40);
24+
});
25+
});
26+
27+
describe("cn", () => {
28+
it("merges class names", () => {
29+
expect(cn("foo", "bar")).toBe("foo bar");
30+
});
31+
32+
it("handles undefined and null inputs", () => {
33+
expect(cn("foo", undefined, null, "bar")).toBe("foo bar");
34+
});
35+
36+
it("resolves conflicting Tailwind classes with tailwind-merge", () => {
37+
expect(cn("px-2", "px-4")).toBe("px-4");
38+
});
39+
});

src/client_logic/template_hook.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ export async function neonTemplateHook({
99
appId: number;
1010
appName: string;
1111
}) {
12-
console.log("Creating Neon project");
1312
const neonProject = await IpcClient.getInstance().createNeonProject({
1413
name: appName,
1514
appId: appId,
1615
});
1716

18-
console.log("Neon project created", neonProject);
1917
await IpcClient.getInstance().setAppEnvVars({
2018
appId: appId,
2119
envVars: [
@@ -41,5 +39,4 @@ export async function neonTemplateHook({
4139
},
4240
],
4341
});
44-
console.log("App env vars set");
4542
}

src/components/ChatList.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ export function ChatList({ show }: { show?: boolean }) {
5757
if (isChatRoute) {
5858
const id = routerState.location.search.id;
5959
if (id) {
60-
console.log("Setting selected chat id to", id);
61-
setSelectedChatId(id);
60+
setSelectedChatId(id);
6261
}
6362
}
6463
}, [isChatRoute, routerState.location.search, setSelectedChatId]);

src/components/ChatPanel.tsx

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,6 @@ export function ChatPanel({
4444
// State to track system messages for auto-scrolling
4545
const [systemMessageCount, setSystemMessageCount] = useState(0);
4646

47-
// Debug logging
48-
console.log("ChatPanel render:", {
49-
isBackendMode,
50-
isFullstackMode,
51-
isFrontendMode,
52-
isTodoPanelOpen,
53-
isStepsPanelOpen,
54-
showTodoToggle: isFullstackMode || isFrontendMode,
55-
});
5647
// Reference to store the processed prompt so we don't submit it twice
5748

5849
const messagesEndRef = useRef<HTMLDivElement | null>(null);
@@ -89,7 +80,6 @@ export function ChatPanel({
8980
};
9081

9182
useEffect(() => {
92-
console.log("streamCount", streamCount);
9383
scrollToBottom();
9484
}, [streamCount]);
9585

src/components/TodoListPanel.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,8 @@ export function TodoListPanel({ isOpen, onClose }: TodoListPanelProps) {
2424
const [editTitle, setEditTitle] = useState("");
2525
const [editDescription, setEditDescription] = useState("");
2626

27-
// Debug logging
28-
console.log("TodoListPanel render:", {
29-
isOpen,
30-
chatId,
31-
todosCount: todos.length,
32-
});
33-
3427
// Filter todos for current chat
3528
const currentChatTodos = todos.filter((todo) => todo.chatId === chatId);
36-
console.log("Current chat todos:", currentChatTodos.length);
3729

3830
const addTodo = () => {
3931
if (!newTodoTitle.trim() || !chatId) return;

src/components/backend-chat/BackendChatPanel.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ export function BackendChatPanel({
3131
const [error, setError] = useState<string | null>(null);
3232
const streamCount = useAtomValue(chatStreamCountAtom);
3333

34-
// Debug logging
35-
console.log("BackendChatPanel render:", { isTodoPanelOpen, isStepsPanelOpen });
36-
3734
const messagesEndRef = useRef<HTMLDivElement>(null);
3835
const messagesContainerRef = useRef<HTMLDivElement | null>(null);
3936

@@ -68,7 +65,6 @@ export function BackendChatPanel({
6865
};
6966

7067
useEffect(() => {
71-
console.log("streamCount", streamCount);
7268
scrollToBottom();
7369
}, [streamCount]);
7470

0 commit comments

Comments
 (0)