Skip to content

Commit 9e77a18

Browse files
2 parents 03f728b + 5d74b9b commit 9e77a18

File tree

8 files changed

+92
-32
lines changed

8 files changed

+92
-32
lines changed

.github/workflows/ci.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
pull_request:
7+
8+
jobs:
9+
frontend-build:
10+
runs-on: ubuntu-latest
11+
defaults:
12+
run:
13+
working-directory: async-code-web
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version: '20'
19+
cache: 'yarn'
20+
cache-dependency-path: async-code-web/yarn.lock
21+
- run: yarn install
22+
- run: yarn build
23+
24+
backend-static-check:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v4
28+
- uses: actions/setup-python@v4
29+
with:
30+
python-version: '3.11'
31+
- name: Static analysis
32+
run: python -m compileall -q server

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ Use Claude Code / CodeX CLI to perform multiple tasks in parallel with a Codex-s
44

55
A code agent task management system that provides parallel execution of AI-powered coding tasks. Users can run multiple Claude Code agents simultaneously through a Codex-style web interface, with support for different agents for comparison and evaluation.
66

7+
![async-code-ui](https://github.com/user-attachments/assets/e490c605-681a-4abb-a440-323e15f1a90d)
8+
9+
10+
![async-code-review](https://github.com/user-attachments/assets/bbf71c82-636c-487b-bb51-6ad0b393c2ef)
11+
12+
713
## Key Features
814

915
- 🤖 **Multi-Agent Support**: Run Claude Code and other AI agents in parallel

async-code-web/app/projects/[id]/tasks/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ export default function ProjectTasksPage() {
205205
</span>
206206
</div>
207207
<p className="text-sm font-medium text-slate-900 truncate mb-1">
208-
{task.chat_messages?.[0]?.content || 'No prompt available'}
208+
{(task.chat_messages as any[])?.[0]?.content || 'No prompt available'}
209209
</p>
210210
<div className="flex items-center gap-4 text-xs text-slate-500">
211211
<span>Created: {new Date(task.created_at || '').toLocaleString()}</span>

async-code-web/app/signin/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { Auth } from '@supabase/auth-ui-react'
44
import { ThemeSupa } from '@supabase/auth-ui-shared'
5-
import { supabase } from '@/lib/supabase'
5+
import { getSupabase } from '@/lib/supabase'
66
import { useAuth } from '@/contexts/auth-context'
77
import { useRouter } from 'next/navigation'
88
import { useEffect } from 'react'
@@ -57,7 +57,7 @@ export default function SignIn() {
5757
</CardHeader>
5858
<CardContent>
5959
<Auth
60-
supabaseClient={supabase}
60+
supabaseClient={getSupabase()}
6161
appearance={{
6262
theme: ThemeSupa,
6363
variables: {

async-code-web/contexts/auth-context.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import React, { createContext, useContext, useEffect, useState } from 'react'
44
import { User, Session } from '@supabase/supabase-js'
5-
import { supabase } from '@/lib/supabase'
5+
import { getSupabase } from '@/lib/supabase'
66

77
interface AuthContextType {
88
user: User | null
@@ -31,6 +31,8 @@ export function AuthProvider({ children }: AuthProviderProps) {
3131
const [loading, setLoading] = useState(true)
3232

3333
useEffect(() => {
34+
const supabase = getSupabase()
35+
3436
// Get initial session
3537
supabase.auth.getSession().then(({ data: { session } }) => {
3638
setSession(session)
@@ -51,6 +53,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
5153
}, [])
5254

5355
const signOut = async () => {
56+
const supabase = getSupabase()
5457
await supabase.auth.signOut()
5558
}
5659

async-code-web/eslint.config.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ const compat = new FlatCompat({
1111

1212
const eslintConfig = [
1313
...compat.extends("next/core-web-vitals", "next/typescript"),
14+
{
15+
rules: {
16+
"react/no-unescaped-entities": "off",
17+
"@typescript-eslint/no-unused-vars": "off",
18+
"react-hooks/exhaustive-deps": "off",
19+
"@typescript-eslint/no-explicit-any": "off",
20+
},
21+
},
1422
];
1523

1624
export default eslintConfig;

async-code-web/lib/supabase-service.ts

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
import { supabase } from './supabase'
1+
import { getSupabase } from './supabase'
22
import { Project, Task, ProjectWithStats, ChatMessage } from '@/types'
33

44
export class SupabaseService {
5+
private static get supabase() {
6+
return getSupabase()
7+
}
58
// Project operations
69
static async getProjects(): Promise<ProjectWithStats[]> {
7-
const { data, error } = await supabase
10+
const { data, error } = await this.supabase
811
.from('projects')
912
.select(`
1013
*,
@@ -35,10 +38,10 @@ export class SupabaseService {
3538
settings?: any
3639
}): Promise<Project> {
3740
// Get current authenticated user
38-
const { data: { user } } = await supabase.auth.getUser()
41+
const { data: { user } } = await this.supabase.auth.getUser()
3942
if (!user) throw new Error('No authenticated user')
4043

41-
const { data, error } = await supabase
44+
const { data, error } = await this.supabase
4245
.from('projects')
4346
.insert([{ ...projectData, user_id: user.id }])
4447
.select()
@@ -49,7 +52,7 @@ export class SupabaseService {
4952
}
5053

5154
static async updateProject(id: number, updates: Partial<Project>): Promise<Project> {
52-
const { data, error } = await supabase
55+
const { data, error } = await this.supabase
5356
.from('projects')
5457
.update(updates)
5558
.eq('id', id)
@@ -61,7 +64,7 @@ export class SupabaseService {
6164
}
6265

6366
static async deleteProject(id: number): Promise<void> {
64-
const { error } = await supabase
67+
const { error } = await this.supabase
6568
.from('projects')
6669
.delete()
6770
.eq('id', id)
@@ -70,7 +73,7 @@ export class SupabaseService {
7073
}
7174

7275
static async getProject(id: number): Promise<Project | null> {
73-
const { data, error } = await supabase
76+
const { data, error } = await this.supabase
7477
.from('projects')
7578
.select('*')
7679
.eq('id', id)
@@ -86,10 +89,10 @@ export class SupabaseService {
8689
// Task operations
8790
static async getTasks(projectId?: number): Promise<Task[]> {
8891
// Get current authenticated user
89-
const { data: { user } } = await supabase.auth.getUser()
92+
const { data: { user } } = await this.supabase.auth.getUser()
9093
if (!user) throw new Error('No authenticated user')
9194

92-
let query = supabase
95+
let query = this.supabase
9396
.from('tasks')
9497
.select(`
9598
*,
@@ -113,7 +116,7 @@ export class SupabaseService {
113116
}
114117

115118
static async getTask(id: number): Promise<Task | null> {
116-
const { data, error } = await supabase
119+
const { data, error } = await this.supabase
117120
.from('tasks')
118121
.select(`
119122
*,
@@ -142,10 +145,10 @@ export class SupabaseService {
142145
chat_messages?: ChatMessage[]
143146
}): Promise<Task> {
144147
// Get current authenticated user
145-
const { data: { user } } = await supabase.auth.getUser()
148+
const { data: { user } } = await this.supabase.auth.getUser()
146149
if (!user) throw new Error('No authenticated user')
147150

148-
const { data, error } = await supabase
151+
const { data, error } = await this.supabase
149152
.from('tasks')
150153
.insert([{
151154
...taskData,
@@ -161,7 +164,7 @@ export class SupabaseService {
161164
}
162165

163166
static async updateTask(id: number, updates: Partial<Task>): Promise<Task> {
164-
const { data, error } = await supabase
167+
const { data, error } = await this.supabase
165168
.from('tasks')
166169
.update(updates)
167170
.eq('id', id)
@@ -174,7 +177,7 @@ export class SupabaseService {
174177

175178
static async addChatMessage(taskId: number, message: ChatMessage): Promise<Task> {
176179
// First get the current task to get existing messages
177-
const { data: task, error: fetchError } = await supabase
180+
const { data: task, error: fetchError } = await this.supabase
178181
.from('tasks')
179182
.select('chat_messages')
180183
.eq('id', taskId)
@@ -185,7 +188,7 @@ export class SupabaseService {
185188
const existingMessages = (task.chat_messages as unknown as ChatMessage[]) || []
186189
const updatedMessages = [...existingMessages, message]
187190

188-
const { data, error } = await supabase
191+
const { data, error } = await this.supabase
189192
.from('tasks')
190193
.update({
191194
chat_messages: updatedMessages as any,
@@ -201,15 +204,15 @@ export class SupabaseService {
201204

202205
// User operations
203206
static async getCurrentUser() {
204-
const { data: { user } } = await supabase.auth.getUser()
207+
const { data: { user } } = await this.supabase.auth.getUser()
205208
return user
206209
}
207210

208211
static async getUserProfile() {
209-
const { data: { user } } = await supabase.auth.getUser()
212+
const { data: { user } } = await this.supabase.auth.getUser()
210213
if (!user) return null
211214

212-
const { data, error } = await supabase
215+
const { data, error } = await this.supabase
213216
.from('users')
214217
.select('*')
215218
.eq('id', user.id)
@@ -228,10 +231,10 @@ export class SupabaseService {
228231
github_token?: string
229232
preferences?: any
230233
}) {
231-
const { data: { user } } = await supabase.auth.getUser()
234+
const { data: { user } } = await this.supabase.auth.getUser()
232235
if (!user) throw new Error('No authenticated user')
233236

234-
const { data, error } = await supabase
237+
const { data, error } = await this.supabase
235238
.from('users')
236239
.update(updates)
237240
.eq('id', user.id)

async-code-web/lib/supabase.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import { createClient } from '@supabase/supabase-js'
22
import { Database } from '@/types/supabase'
33

4-
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
5-
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
4+
let supabaseInstance: ReturnType<typeof createClient<Database>> | null = null
65

7-
export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey, {
8-
auth: {
9-
autoRefreshToken: true,
10-
persistSession: true,
11-
detectSessionInUrl: true
6+
export const getSupabase = () => {
7+
if (!supabaseInstance) {
8+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
9+
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
10+
11+
supabaseInstance = createClient<Database>(supabaseUrl, supabaseAnonKey, {
12+
auth: {
13+
autoRefreshToken: true,
14+
persistSession: true,
15+
detectSessionInUrl: true
16+
}
17+
})
1218
}
13-
})
19+
20+
return supabaseInstance
21+
}

0 commit comments

Comments
 (0)