codebakers 2.5.4 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -255
- package/dist/chunk-HOWR3YTF.js +146 -0
- package/dist/index.d.ts +0 -3
- package/dist/index.js +10505 -7997
- package/dist/terminal-6ZQVP6R7.js +10 -0
- package/package.json +26 -41
- package/AUDIT_REPORT.md +0 -138
- package/dist/advisors-RWRTSJRR.js +0 -7
- package/dist/chunk-ASIJIQYC.js +0 -320
- package/dist/chunk-D44U3IEA.js +0 -565
- package/dist/chunk-LANM5XQW.js +0 -326
- package/dist/prd-RYITSL6Q.js +0 -7
- package/install.bat +0 -9
- package/installers/CodeBakers-Install.bat +0 -207
- package/installers/CodeBakers-Install.command +0 -232
- package/installers/README.md +0 -157
- package/installers/mac/assets/README.txt +0 -31
- package/installers/mac/build-mac-installer.sh +0 -240
- package/installers/windows/CodeBakers.iss +0 -256
- package/installers/windows/assets/README.txt +0 -16
- package/installers/windows/scripts/post-install.bat +0 -15
- package/src/channels/discord.ts +0 -5
- package/src/channels/slack.ts +0 -5
- package/src/channels/sms.ts +0 -4
- package/src/channels/telegram.ts +0 -5
- package/src/channels/whatsapp.ts +0 -7
- package/src/commands/advisors.ts +0 -699
- package/src/commands/build.ts +0 -1025
- package/src/commands/check.ts +0 -365
- package/src/commands/code.ts +0 -806
- package/src/commands/connect.ts +0 -12
- package/src/commands/deploy.ts +0 -448
- package/src/commands/design.ts +0 -298
- package/src/commands/fix.ts +0 -20
- package/src/commands/gateway.ts +0 -604
- package/src/commands/generate.ts +0 -178
- package/src/commands/init.ts +0 -634
- package/src/commands/integrate.ts +0 -884
- package/src/commands/learn.ts +0 -36
- package/src/commands/migrate.ts +0 -419
- package/src/commands/prd-maker.ts +0 -588
- package/src/commands/prd.ts +0 -419
- package/src/commands/security.ts +0 -102
- package/src/commands/setup.ts +0 -600
- package/src/commands/status.ts +0 -56
- package/src/commands/website.ts +0 -741
- package/src/index.ts +0 -627
- package/src/patterns/loader.ts +0 -337
- package/src/services/github.ts +0 -61
- package/src/services/supabase.ts +0 -147
- package/src/services/vercel.ts +0 -61
- package/src/utils/claude-md.ts +0 -287
- package/src/utils/config.ts +0 -375
- package/src/utils/display.ts +0 -338
- package/src/utils/files.ts +0 -418
- package/src/utils/nlp.ts +0 -312
- package/src/utils/ui.ts +0 -441
- package/src/utils/updates.ts +0 -8
- package/src/utils/voice.ts +0 -323
- package/tsconfig.json +0 -26
package/src/patterns/loader.ts
DELETED
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import { Config } from '../utils/config.js';
|
|
4
|
-
|
|
5
|
-
// Embedded core patterns (extracted from your CLAUDE.md)
|
|
6
|
-
const CORE_PATTERNS = `
|
|
7
|
-
# CODEBAKERS CORE PATTERNS
|
|
8
|
-
|
|
9
|
-
## ABSOLUTE PROHIBITIONS
|
|
10
|
-
|
|
11
|
-
These will NEVER appear in your code:
|
|
12
|
-
|
|
13
|
-
\`\`\`typescript
|
|
14
|
-
// ❌ BANNED - NON-FUNCTIONAL CODE
|
|
15
|
-
onClick={handleClick} // where handleClick doesn't exist
|
|
16
|
-
onSubmit={handleSubmit} // where handleSubmit doesn't exist
|
|
17
|
-
href="/some-page" // where the page doesn't exist
|
|
18
|
-
|
|
19
|
-
// ❌ BANNED - INCOMPLETE CODE
|
|
20
|
-
TODO: // No TODOs ever
|
|
21
|
-
FIXME: // No FIXMEs ever
|
|
22
|
-
// ... // No placeholder comments
|
|
23
|
-
throw new Error('Not implemented')
|
|
24
|
-
|
|
25
|
-
// ❌ BANNED - DEBUG CODE
|
|
26
|
-
console.log('test') // No debug logs
|
|
27
|
-
debugger; // No debugger statements
|
|
28
|
-
|
|
29
|
-
// ❌ BANNED - TYPE SAFETY VIOLATIONS
|
|
30
|
-
any // No 'any' types
|
|
31
|
-
@ts-ignore // No ignoring TypeScript
|
|
32
|
-
as any // No casting to any
|
|
33
|
-
|
|
34
|
-
// ❌ BANNED - SECURITY VIOLATIONS
|
|
35
|
-
eval() // No eval ever
|
|
36
|
-
innerHTML = // No direct innerHTML
|
|
37
|
-
dangerouslySetInnerHTML // Without DOMPurify
|
|
38
|
-
\`\`\`
|
|
39
|
-
|
|
40
|
-
## MANDATORY PATTERNS
|
|
41
|
-
|
|
42
|
-
### Every Button MUST Have:
|
|
43
|
-
|
|
44
|
-
\`\`\`typescript
|
|
45
|
-
<Button
|
|
46
|
-
onClick={handleAction}
|
|
47
|
-
disabled={isLoading || isDisabled}
|
|
48
|
-
aria-label="Descriptive action name"
|
|
49
|
-
>
|
|
50
|
-
{isLoading ? (
|
|
51
|
-
<>
|
|
52
|
-
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
53
|
-
Processing...
|
|
54
|
-
</>
|
|
55
|
-
) : (
|
|
56
|
-
'Action Name'
|
|
57
|
-
)}
|
|
58
|
-
</Button>
|
|
59
|
-
|
|
60
|
-
// The handler MUST exist and be complete:
|
|
61
|
-
const handleAction = async () => {
|
|
62
|
-
setIsLoading(true);
|
|
63
|
-
try {
|
|
64
|
-
await performAction();
|
|
65
|
-
toast.success('Action completed successfully');
|
|
66
|
-
} catch (error) {
|
|
67
|
-
const message = error instanceof Error ? error.message : 'Action failed';
|
|
68
|
-
toast.error(message);
|
|
69
|
-
} finally {
|
|
70
|
-
setIsLoading(false);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
\`\`\`
|
|
74
|
-
|
|
75
|
-
### Every Form MUST Have:
|
|
76
|
-
|
|
77
|
-
1. React Hook Form with Zod validation
|
|
78
|
-
2. Loading state during submission
|
|
79
|
-
3. Error messages for each field
|
|
80
|
-
4. Success/error toast notifications
|
|
81
|
-
5. Proper disabled state while submitting
|
|
82
|
-
|
|
83
|
-
### Every Async Operation MUST Have:
|
|
84
|
-
|
|
85
|
-
\`\`\`typescript
|
|
86
|
-
const [data, setData] = useState<DataType | null>(null);
|
|
87
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
88
|
-
const [error, setError] = useState<string | null>(null);
|
|
89
|
-
|
|
90
|
-
const fetchData = async () => {
|
|
91
|
-
setIsLoading(true);
|
|
92
|
-
setError(null);
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
const response = await fetch('/api/data');
|
|
96
|
-
if (!response.ok) throw new Error(\`HTTP \${response.status}\`);
|
|
97
|
-
const result = await response.json();
|
|
98
|
-
setData(result.data);
|
|
99
|
-
} catch (err) {
|
|
100
|
-
const message = err instanceof Error ? err.message : 'Failed to fetch';
|
|
101
|
-
setError(message);
|
|
102
|
-
toast.error(message);
|
|
103
|
-
} finally {
|
|
104
|
-
setIsLoading(false);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
\`\`\`
|
|
108
|
-
|
|
109
|
-
### Every List MUST Have:
|
|
110
|
-
|
|
111
|
-
1. Loading state with spinner/skeleton
|
|
112
|
-
2. Error state with retry button
|
|
113
|
-
3. Empty state with helpful message
|
|
114
|
-
4. Success state with data
|
|
115
|
-
|
|
116
|
-
## FILE STRUCTURE
|
|
117
|
-
|
|
118
|
-
\`\`\`
|
|
119
|
-
src/
|
|
120
|
-
├── app/ # Next.js App Router
|
|
121
|
-
│ ├── (auth)/ # Routes requiring authentication
|
|
122
|
-
│ ├── (public)/ # Public routes
|
|
123
|
-
│ ├── (marketing)/ # Marketing pages
|
|
124
|
-
│ ├── api/ # API routes
|
|
125
|
-
│ └── layout.tsx # Root layout
|
|
126
|
-
├── components/
|
|
127
|
-
│ ├── ui/ # shadcn/ui components
|
|
128
|
-
│ ├── forms/ # Form components
|
|
129
|
-
│ ├── layouts/ # Layout components
|
|
130
|
-
│ └── [feature]/ # Feature-specific components
|
|
131
|
-
├── lib/
|
|
132
|
-
│ ├── supabase/ # Supabase clients
|
|
133
|
-
│ ├── utils.ts # Utility functions
|
|
134
|
-
│ └── validations.ts # Zod schemas
|
|
135
|
-
├── hooks/ # Custom React hooks
|
|
136
|
-
├── types/ # TypeScript types
|
|
137
|
-
├── stores/ # Zustand stores
|
|
138
|
-
└── services/ # Business logic
|
|
139
|
-
\`\`\`
|
|
140
|
-
|
|
141
|
-
## API ROUTE PATTERN
|
|
142
|
-
|
|
143
|
-
\`\`\`typescript
|
|
144
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
145
|
-
import { z } from 'zod';
|
|
146
|
-
import { createServerClient } from '@/lib/supabase/server';
|
|
147
|
-
|
|
148
|
-
const requestSchema = z.object({
|
|
149
|
-
// Define request body schema
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
export async function POST(request: NextRequest) {
|
|
153
|
-
try {
|
|
154
|
-
// 1. Auth check
|
|
155
|
-
const supabase = createServerClient();
|
|
156
|
-
const { data: { user }, error: authError } = await supabase.auth.getUser();
|
|
157
|
-
|
|
158
|
-
if (authError || !user) {
|
|
159
|
-
return NextResponse.json(
|
|
160
|
-
{ error: 'Unauthorized' },
|
|
161
|
-
{ status: 401 }
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// 2. Validate input
|
|
166
|
-
const body = await request.json();
|
|
167
|
-
const result = requestSchema.safeParse(body);
|
|
168
|
-
|
|
169
|
-
if (!result.success) {
|
|
170
|
-
return NextResponse.json(
|
|
171
|
-
{ error: 'Validation failed', details: result.error.flatten() },
|
|
172
|
-
{ status: 400 }
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// 3. Business logic
|
|
177
|
-
const data = result.data;
|
|
178
|
-
// ... do something
|
|
179
|
-
|
|
180
|
-
// 4. Return success
|
|
181
|
-
return NextResponse.json({ data }, { status: 201 });
|
|
182
|
-
|
|
183
|
-
} catch (error) {
|
|
184
|
-
console.error('[API] Error:', error);
|
|
185
|
-
return NextResponse.json(
|
|
186
|
-
{ error: 'Internal server error' },
|
|
187
|
-
{ status: 500 }
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
\`\`\`
|
|
192
|
-
|
|
193
|
-
## SUPABASE RLS PATTERN
|
|
194
|
-
|
|
195
|
-
Every table MUST have Row Level Security enabled:
|
|
196
|
-
|
|
197
|
-
\`\`\`sql
|
|
198
|
-
-- Enable RLS
|
|
199
|
-
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
|
|
200
|
-
|
|
201
|
-
-- User can only see their own data
|
|
202
|
-
CREATE POLICY "Users can view own data"
|
|
203
|
-
ON your_table FOR SELECT
|
|
204
|
-
TO authenticated
|
|
205
|
-
USING (user_id = auth.uid());
|
|
206
|
-
|
|
207
|
-
-- User can only insert their own data
|
|
208
|
-
CREATE POLICY "Users can insert own data"
|
|
209
|
-
ON your_table FOR INSERT
|
|
210
|
-
TO authenticated
|
|
211
|
-
WITH CHECK (user_id = auth.uid());
|
|
212
|
-
|
|
213
|
-
-- User can only update their own data
|
|
214
|
-
CREATE POLICY "Users can update own data"
|
|
215
|
-
ON your_table FOR UPDATE
|
|
216
|
-
TO authenticated
|
|
217
|
-
USING (user_id = auth.uid())
|
|
218
|
-
WITH CHECK (user_id = auth.uid());
|
|
219
|
-
|
|
220
|
-
-- User can only delete their own data
|
|
221
|
-
CREATE POLICY "Users can delete own data"
|
|
222
|
-
ON your_table FOR DELETE
|
|
223
|
-
TO authenticated
|
|
224
|
-
USING (user_id = auth.uid());
|
|
225
|
-
\`\`\`
|
|
226
|
-
|
|
227
|
-
## STATE MANAGEMENT
|
|
228
|
-
|
|
229
|
-
Use Zustand for global state:
|
|
230
|
-
|
|
231
|
-
\`\`\`typescript
|
|
232
|
-
import { create } from 'zustand';
|
|
233
|
-
|
|
234
|
-
interface AppState {
|
|
235
|
-
user: User | null;
|
|
236
|
-
isLoading: boolean;
|
|
237
|
-
setUser: (user: User | null) => void;
|
|
238
|
-
reset: () => void;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export const useAppStore = create<AppState>((set) => ({
|
|
242
|
-
user: null,
|
|
243
|
-
isLoading: false,
|
|
244
|
-
setUser: (user) => set({ user }),
|
|
245
|
-
reset: () => set({ user: null, isLoading: false }),
|
|
246
|
-
}));
|
|
247
|
-
\`\`\`
|
|
248
|
-
|
|
249
|
-
## ERROR HANDLING
|
|
250
|
-
|
|
251
|
-
Use typed error classes:
|
|
252
|
-
|
|
253
|
-
\`\`\`typescript
|
|
254
|
-
export class AppError extends Error {
|
|
255
|
-
constructor(
|
|
256
|
-
message: string,
|
|
257
|
-
public code: string,
|
|
258
|
-
public statusCode: number = 500
|
|
259
|
-
) {
|
|
260
|
-
super(message);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export class AuthenticationError extends AppError {
|
|
265
|
-
constructor(message = 'Authentication required') {
|
|
266
|
-
super(message, 'AUTH_ERROR', 401);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
export class ValidationError extends AppError {
|
|
271
|
-
constructor(message = 'Validation failed', public details?: Record<string, string[]>) {
|
|
272
|
-
super(message, 'VALIDATION_ERROR', 400);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
\`\`\`
|
|
276
|
-
`;
|
|
277
|
-
|
|
278
|
-
export async function loadPatterns(config: Config): Promise<string> {
|
|
279
|
-
const projectDir = process.cwd();
|
|
280
|
-
const patterns: string[] = [CORE_PATTERNS];
|
|
281
|
-
|
|
282
|
-
// Load project-specific patterns if they exist
|
|
283
|
-
const projectPatternDir = path.join(projectDir, '.codebakers', 'patterns');
|
|
284
|
-
if (await fs.pathExists(projectPatternDir)) {
|
|
285
|
-
const files = await fs.readdir(projectPatternDir);
|
|
286
|
-
for (const file of files) {
|
|
287
|
-
if (file.endsWith('.md')) {
|
|
288
|
-
const content = await fs.readFile(path.join(projectPatternDir, file), 'utf-8');
|
|
289
|
-
patterns.push(content);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Load global patterns
|
|
295
|
-
const globalPatternDir = config.getPatternsDir();
|
|
296
|
-
if (await fs.pathExists(globalPatternDir)) {
|
|
297
|
-
const files = await fs.readdir(globalPatternDir);
|
|
298
|
-
for (const file of files.slice(0, 10)) { // Limit to avoid context overflow
|
|
299
|
-
if (file.endsWith('.md')) {
|
|
300
|
-
const content = await fs.readFile(path.join(globalPatternDir, file), 'utf-8');
|
|
301
|
-
// Truncate if too long
|
|
302
|
-
patterns.push(content.slice(0, 10000));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return patterns.join('\n\n---\n\n');
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export async function validateAgainstPatterns(
|
|
311
|
-
code: string,
|
|
312
|
-
patterns: string
|
|
313
|
-
): Promise<{ valid: boolean; violations: string[] }> {
|
|
314
|
-
const violations: string[] = [];
|
|
315
|
-
|
|
316
|
-
// Check for banned patterns
|
|
317
|
-
if (code.includes(': any') && !code.includes('// @allow-any')) {
|
|
318
|
-
violations.push('Use of "any" type detected');
|
|
319
|
-
}
|
|
320
|
-
if (code.includes('@ts-ignore')) {
|
|
321
|
-
violations.push('@ts-ignore detected');
|
|
322
|
-
}
|
|
323
|
-
if (/console\.log\(/.test(code)) {
|
|
324
|
-
violations.push('console.log detected');
|
|
325
|
-
}
|
|
326
|
-
if (/TODO:|FIXME:/.test(code)) {
|
|
327
|
-
violations.push('TODO/FIXME comment detected');
|
|
328
|
-
}
|
|
329
|
-
if (/eval\s*\(/.test(code)) {
|
|
330
|
-
violations.push('eval() detected - security risk');
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
return {
|
|
334
|
-
valid: violations.length === 0,
|
|
335
|
-
violations,
|
|
336
|
-
};
|
|
337
|
-
}
|
package/src/services/github.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { Octokit } from '@octokit/rest';
|
|
2
|
-
import { Config } from '../utils/config.js';
|
|
3
|
-
|
|
4
|
-
export class GitHubService {
|
|
5
|
-
private octokit: Octokit;
|
|
6
|
-
private config: Config;
|
|
7
|
-
|
|
8
|
-
constructor(config: Config) {
|
|
9
|
-
this.config = config;
|
|
10
|
-
const creds = config.getCredentials('github');
|
|
11
|
-
|
|
12
|
-
if (!creds?.token) {
|
|
13
|
-
throw new Error('GitHub not configured. Run `codebakers setup`.');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
this.octokit = new Octokit({ auth: creds.token });
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async createRepo(name: string, options: { private?: boolean } = {}): Promise<{
|
|
20
|
-
id: number;
|
|
21
|
-
name: string;
|
|
22
|
-
full_name: string;
|
|
23
|
-
html_url: string;
|
|
24
|
-
clone_url: string;
|
|
25
|
-
}> {
|
|
26
|
-
const response = await this.octokit.repos.createForAuthenticatedUser({
|
|
27
|
-
name,
|
|
28
|
-
private: options.private ?? true,
|
|
29
|
-
auto_init: false,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
id: response.data.id,
|
|
34
|
-
name: response.data.name,
|
|
35
|
-
full_name: response.data.full_name,
|
|
36
|
-
html_url: response.data.html_url,
|
|
37
|
-
clone_url: response.data.clone_url,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async getUser(): Promise<{ login: string; name: string | null }> {
|
|
42
|
-
const response = await this.octokit.users.getAuthenticated();
|
|
43
|
-
return {
|
|
44
|
-
login: response.data.login,
|
|
45
|
-
name: response.data.name,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async listRepos(): Promise<Array<{ name: string; full_name: string; private: boolean }>> {
|
|
50
|
-
const response = await this.octokit.repos.listForAuthenticatedUser({
|
|
51
|
-
sort: 'updated',
|
|
52
|
-
per_page: 100,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
return response.data.map(repo => ({
|
|
56
|
-
name: repo.name,
|
|
57
|
-
full_name: repo.full_name,
|
|
58
|
-
private: repo.private,
|
|
59
|
-
}));
|
|
60
|
-
}
|
|
61
|
-
}
|
package/src/services/supabase.ts
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { Config } from '../utils/config.js';
|
|
2
|
-
|
|
3
|
-
export class SupabaseService {
|
|
4
|
-
private config: Config;
|
|
5
|
-
private accessToken: string;
|
|
6
|
-
|
|
7
|
-
constructor(config: Config) {
|
|
8
|
-
this.config = config;
|
|
9
|
-
const creds = config.getCredentials('supabase');
|
|
10
|
-
|
|
11
|
-
if (!creds?.accessToken) {
|
|
12
|
-
throw new Error('Supabase not configured. Run `codebakers setup`.');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
this.accessToken = creds.accessToken;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async createProject(name: string): Promise<{
|
|
19
|
-
id: string;
|
|
20
|
-
name: string;
|
|
21
|
-
api_url: string;
|
|
22
|
-
anon_key: string;
|
|
23
|
-
}> {
|
|
24
|
-
// Generate a random password for the database
|
|
25
|
-
const dbPassword = this.generatePassword();
|
|
26
|
-
|
|
27
|
-
const response = await fetch('https://api.supabase.com/v1/projects', {
|
|
28
|
-
method: 'POST',
|
|
29
|
-
headers: {
|
|
30
|
-
Authorization: `Bearer ${this.accessToken}`,
|
|
31
|
-
'Content-Type': 'application/json',
|
|
32
|
-
},
|
|
33
|
-
body: JSON.stringify({
|
|
34
|
-
name,
|
|
35
|
-
organization_id: await this.getDefaultOrgId(),
|
|
36
|
-
region: 'us-west-1',
|
|
37
|
-
plan: 'free',
|
|
38
|
-
db_pass: dbPassword,
|
|
39
|
-
}),
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!response.ok) {
|
|
43
|
-
const error = await response.json();
|
|
44
|
-
throw new Error(error.message || 'Failed to create Supabase project');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const data = await response.json();
|
|
48
|
-
|
|
49
|
-
// Wait for project to be ready
|
|
50
|
-
await this.waitForProject(data.id);
|
|
51
|
-
|
|
52
|
-
// Get API keys
|
|
53
|
-
const keys = await this.getApiKeys(data.id);
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
id: data.id,
|
|
57
|
-
name: data.name,
|
|
58
|
-
api_url: `https://${data.id}.supabase.co`,
|
|
59
|
-
anon_key: keys.anon_key,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private async getDefaultOrgId(): Promise<string> {
|
|
64
|
-
const response = await fetch('https://api.supabase.com/v1/organizations', {
|
|
65
|
-
headers: { Authorization: `Bearer ${this.accessToken}` },
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
if (!response.ok) {
|
|
69
|
-
throw new Error('Failed to get organizations');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const orgs = await response.json();
|
|
73
|
-
if (orgs.length === 0) {
|
|
74
|
-
throw new Error('No Supabase organization found');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return orgs[0].id;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
private async waitForProject(projectId: string, maxWait = 120000): Promise<void> {
|
|
81
|
-
const startTime = Date.now();
|
|
82
|
-
|
|
83
|
-
while (Date.now() - startTime < maxWait) {
|
|
84
|
-
const response = await fetch(
|
|
85
|
-
`https://api.supabase.com/v1/projects/${projectId}`,
|
|
86
|
-
{ headers: { Authorization: `Bearer ${this.accessToken}` } }
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
if (response.ok) {
|
|
90
|
-
const project = await response.json();
|
|
91
|
-
if (project.status === 'ACTIVE_HEALTHY') {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
throw new Error('Timeout waiting for Supabase project');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private async getApiKeys(projectId: string): Promise<{ anon_key: string; service_role_key: string }> {
|
|
103
|
-
const response = await fetch(
|
|
104
|
-
`https://api.supabase.com/v1/projects/${projectId}/api-keys`,
|
|
105
|
-
{ headers: { Authorization: `Bearer ${this.accessToken}` } }
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
if (!response.ok) {
|
|
109
|
-
throw new Error('Failed to get API keys');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const keys = await response.json();
|
|
113
|
-
const anonKey = keys.find((k: any) => k.name === 'anon');
|
|
114
|
-
const serviceKey = keys.find((k: any) => k.name === 'service_role');
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
anon_key: anonKey?.api_key || '',
|
|
118
|
-
service_role_key: serviceKey?.api_key || '',
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private generatePassword(): string {
|
|
123
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%';
|
|
124
|
-
let password = '';
|
|
125
|
-
for (let i = 0; i < 24; i++) {
|
|
126
|
-
password += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
127
|
-
}
|
|
128
|
-
return password;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async listProjects(): Promise<Array<{ id: string; name: string; region: string }>> {
|
|
132
|
-
const response = await fetch('https://api.supabase.com/v1/projects', {
|
|
133
|
-
headers: { Authorization: `Bearer ${this.accessToken}` },
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
if (!response.ok) {
|
|
137
|
-
throw new Error('Failed to list projects');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const projects = await response.json();
|
|
141
|
-
return projects.map((p: any) => ({
|
|
142
|
-
id: p.id,
|
|
143
|
-
name: p.name,
|
|
144
|
-
region: p.region,
|
|
145
|
-
}));
|
|
146
|
-
}
|
|
147
|
-
}
|
package/src/services/vercel.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { execa } from 'execa';
|
|
2
|
-
import { Config } from '../utils/config.js';
|
|
3
|
-
|
|
4
|
-
export class VercelService {
|
|
5
|
-
private config: Config;
|
|
6
|
-
private token: string;
|
|
7
|
-
|
|
8
|
-
constructor(config: Config) {
|
|
9
|
-
this.config = config;
|
|
10
|
-
const creds = config.getCredentials('vercel');
|
|
11
|
-
|
|
12
|
-
if (!creds?.token) {
|
|
13
|
-
throw new Error('Vercel not configured. Run `codebakers setup`.');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
this.token = creds.token;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async createProject(name: string): Promise<{ id: string; name: string }> {
|
|
20
|
-
const response = await fetch('https://api.vercel.com/v9/projects', {
|
|
21
|
-
method: 'POST',
|
|
22
|
-
headers: {
|
|
23
|
-
Authorization: `Bearer ${this.token}`,
|
|
24
|
-
'Content-Type': 'application/json',
|
|
25
|
-
},
|
|
26
|
-
body: JSON.stringify({ name, framework: 'nextjs' }),
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
if (!response.ok) {
|
|
30
|
-
const error = await response.json();
|
|
31
|
-
throw new Error(error.error?.message || 'Failed to create project');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const data = await response.json();
|
|
35
|
-
return { id: data.id, name: data.name };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async deploy(projectPath: string, production = false): Promise<{ url: string }> {
|
|
39
|
-
const args = ['vercel', '--yes'];
|
|
40
|
-
if (production) args.push('--prod');
|
|
41
|
-
|
|
42
|
-
const result = await execa('npx', args, {
|
|
43
|
-
cwd: projectPath,
|
|
44
|
-
env: { ...process.env, VERCEL_TOKEN: this.token },
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const urlMatch = result.stdout.match(/https:\/\/[^\s]+\.vercel\.app/);
|
|
48
|
-
return { url: urlMatch ? urlMatch[0] : 'https://vercel.app' };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async addDomain(projectName: string, domain: string): Promise<void> {
|
|
52
|
-
await fetch(`https://api.vercel.com/v10/projects/${projectName}/domains`, {
|
|
53
|
-
method: 'POST',
|
|
54
|
-
headers: {
|
|
55
|
-
Authorization: `Bearer ${this.token}`,
|
|
56
|
-
'Content-Type': 'application/json',
|
|
57
|
-
},
|
|
58
|
-
body: JSON.stringify({ name: domain }),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|