@hatchway/cli 0.50.53

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.
Files changed (80) hide show
  1. package/README.md +274 -0
  2. package/bin/hatchway.js +31 -0
  3. package/dist/chunks/Banner-DL1Fpz_g.js +115 -0
  4. package/dist/chunks/Banner-DL1Fpz_g.js.map +1 -0
  5. package/dist/chunks/auto-update-Ddo5Ntt7.js +264 -0
  6. package/dist/chunks/auto-update-Ddo5Ntt7.js.map +1 -0
  7. package/dist/chunks/build-V8_D-JHF.js +116 -0
  8. package/dist/chunks/build-V8_D-JHF.js.map +1 -0
  9. package/dist/chunks/cleanup-BNuJNSve.js +141 -0
  10. package/dist/chunks/cleanup-BNuJNSve.js.map +1 -0
  11. package/dist/chunks/cli-auth-B4Do-N8Y.js +340 -0
  12. package/dist/chunks/cli-auth-B4Do-N8Y.js.map +1 -0
  13. package/dist/chunks/cli-error-1drkrXNn.js +140 -0
  14. package/dist/chunks/cli-error-1drkrXNn.js.map +1 -0
  15. package/dist/chunks/config-hFJA7z5y.js +167 -0
  16. package/dist/chunks/config-hFJA7z5y.js.map +1 -0
  17. package/dist/chunks/config-manager-DST6RbP8.js +133 -0
  18. package/dist/chunks/config-manager-DST6RbP8.js.map +1 -0
  19. package/dist/chunks/database-YGb1Lzim.js +68 -0
  20. package/dist/chunks/database-YGb1Lzim.js.map +1 -0
  21. package/dist/chunks/database-setup-U31oEs90.js +253 -0
  22. package/dist/chunks/database-setup-U31oEs90.js.map +1 -0
  23. package/dist/chunks/devtools-CPruVlOo.js +75 -0
  24. package/dist/chunks/devtools-CPruVlOo.js.map +1 -0
  25. package/dist/chunks/index-DCC6HGdr.js +119 -0
  26. package/dist/chunks/index-DCC6HGdr.js.map +1 -0
  27. package/dist/chunks/init-DkXJVFFx.js +472 -0
  28. package/dist/chunks/init-DkXJVFFx.js.map +1 -0
  29. package/dist/chunks/init-tui-D2VOVdeK.js +1131 -0
  30. package/dist/chunks/init-tui-D2VOVdeK.js.map +1 -0
  31. package/dist/chunks/logger-6V5cBxba.js +38 -0
  32. package/dist/chunks/logger-6V5cBxba.js.map +1 -0
  33. package/dist/chunks/login-CA1XWUEM.js +63 -0
  34. package/dist/chunks/login-CA1XWUEM.js.map +1 -0
  35. package/dist/chunks/logout-BC4VFt8f.js +40 -0
  36. package/dist/chunks/logout-BC4VFt8f.js.map +1 -0
  37. package/dist/chunks/main-tui-D8KkJRd_.js +648 -0
  38. package/dist/chunks/main-tui-D8KkJRd_.js.map +1 -0
  39. package/dist/chunks/manager-DjVI7erc.js +1161 -0
  40. package/dist/chunks/manager-DjVI7erc.js.map +1 -0
  41. package/dist/chunks/port-allocator-BENntRMG.js +864 -0
  42. package/dist/chunks/port-allocator-BENntRMG.js.map +1 -0
  43. package/dist/chunks/process-killer-ChXAqhfm.js +87 -0
  44. package/dist/chunks/process-killer-ChXAqhfm.js.map +1 -0
  45. package/dist/chunks/prompts-Beijr8dm.js +128 -0
  46. package/dist/chunks/prompts-Beijr8dm.js.map +1 -0
  47. package/dist/chunks/repo-cloner-UY3L2X7h.js +219 -0
  48. package/dist/chunks/repo-cloner-UY3L2X7h.js.map +1 -0
  49. package/dist/chunks/repo-detector-36VydrlB.js +66 -0
  50. package/dist/chunks/repo-detector-36VydrlB.js.map +1 -0
  51. package/dist/chunks/run-Du6dvTJL.js +697 -0
  52. package/dist/chunks/run-Du6dvTJL.js.map +1 -0
  53. package/dist/chunks/runner-logger-instance-Dj_JMznn.js +899 -0
  54. package/dist/chunks/runner-logger-instance-Dj_JMznn.js.map +1 -0
  55. package/dist/chunks/spinner-DTH0QZQw.js +53 -0
  56. package/dist/chunks/spinner-DTH0QZQw.js.map +1 -0
  57. package/dist/chunks/start-Dkuro1jp.js +1713 -0
  58. package/dist/chunks/start-Dkuro1jp.js.map +1 -0
  59. package/dist/chunks/start-traditional-7wlD2f2H.js +255 -0
  60. package/dist/chunks/start-traditional-7wlD2f2H.js.map +1 -0
  61. package/dist/chunks/status-BU3cFJm1.js +97 -0
  62. package/dist/chunks/status-BU3cFJm1.js.map +1 -0
  63. package/dist/chunks/theme-NAQBkisB.js +40222 -0
  64. package/dist/chunks/theme-NAQBkisB.js.map +1 -0
  65. package/dist/chunks/upgrade-BBpJirEu.js +455 -0
  66. package/dist/chunks/upgrade-BBpJirEu.js.map +1 -0
  67. package/dist/chunks/use-app-Ct3w2jLI.js +10 -0
  68. package/dist/chunks/use-app-Ct3w2jLI.js.map +1 -0
  69. package/dist/chunks/useBuildState-Dy7pRR8Z.js +330 -0
  70. package/dist/chunks/useBuildState-Dy7pRR8Z.js.map +1 -0
  71. package/dist/cli/index.js +712 -0
  72. package/dist/cli/index.js.map +1 -0
  73. package/dist/index.js +13625 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/instrument.js +45 -0
  76. package/dist/instrument.js.map +1 -0
  77. package/dist/templates.json +295 -0
  78. package/package.json +87 -0
  79. package/templates/config.template.json +18 -0
  80. package/templates.json +295 -0
@@ -0,0 +1,864 @@
1
+ // Hatchway CLI - Built with Rollup
2
+ import { drizzle } from 'drizzle-orm/node-postgres';
3
+ import pg from 'pg';
4
+ import { pgTable, timestamp, boolean, text, uuid, index, uniqueIndex, integer, jsonb } from 'drizzle-orm/pg-core';
5
+ import { sql } from 'drizzle-orm';
6
+ import 'net';
7
+ import { readFile } from 'fs/promises';
8
+ import { existsSync } from 'fs';
9
+ import { join } from 'path';
10
+ import * as Sentry from '@sentry/node';
11
+
12
+ var __defProp = Object.defineProperty;
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+
18
+ // src/lib/db/schema.ts
19
+ var schema_exports = {};
20
+ __export(schema_exports, {
21
+ accounts: () => accounts,
22
+ cliAuthSessions: () => cliAuthSessions,
23
+ generationNotes: () => generationNotes,
24
+ generationSessions: () => generationSessions,
25
+ generationTodos: () => generationTodos,
26
+ generationToolCalls: () => generationToolCalls,
27
+ githubConnections: () => githubConnections,
28
+ messages: () => messages,
29
+ portAllocations: () => portAllocations,
30
+ projects: () => projects,
31
+ railwayConnections: () => railwayConnections,
32
+ railwayDeployments: () => railwayDeployments,
33
+ runnerKeys: () => runnerKeys,
34
+ runningProcesses: () => runningProcesses,
35
+ serverOperations: () => serverOperations,
36
+ sessions: () => sessions,
37
+ users: () => users,
38
+ verifications: () => verifications
39
+ });
40
+ var users = pgTable("users", {
41
+ id: uuid("id").primaryKey().defaultRandom(),
42
+ name: text("name").notNull(),
43
+ email: text("email").notNull().unique(),
44
+ emailVerified: boolean("email_verified").notNull().default(false),
45
+ image: text("image"),
46
+ hasCompletedOnboarding: boolean("has_completed_onboarding").notNull().default(false),
47
+ createdAt: timestamp("created_at").notNull().defaultNow(),
48
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
49
+ });
50
+ var sessions = pgTable("sessions", {
51
+ id: uuid("id").primaryKey().defaultRandom(),
52
+ userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
53
+ token: text("token").notNull().unique(),
54
+ expiresAt: timestamp("expires_at").notNull(),
55
+ ipAddress: text("ip_address"),
56
+ userAgent: text("user_agent"),
57
+ createdAt: timestamp("created_at").notNull().defaultNow(),
58
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
59
+ }, (table) => ({
60
+ userIdIdx: index("sessions_user_id_idx").on(table.userId),
61
+ tokenIdx: index("sessions_token_idx").on(table.token)
62
+ }));
63
+ var accounts = pgTable("accounts", {
64
+ id: uuid("id").primaryKey().defaultRandom(),
65
+ userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
66
+ accountId: text("account_id").notNull(),
67
+ providerId: text("provider_id").notNull(),
68
+ // 'credential', 'google', etc.
69
+ accessToken: text("access_token"),
70
+ refreshToken: text("refresh_token"),
71
+ accessTokenExpiresAt: timestamp("access_token_expires_at"),
72
+ refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
73
+ scope: text("scope"),
74
+ idToken: text("id_token"),
75
+ // ID token from OAuth providers
76
+ password: text("password"),
77
+ // Hashed password for credential provider
78
+ createdAt: timestamp("created_at").notNull().defaultNow(),
79
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
80
+ }, (table) => ({
81
+ userIdIdx: index("accounts_user_id_idx").on(table.userId),
82
+ providerAccountIdx: uniqueIndex("accounts_provider_account_idx").on(table.providerId, table.accountId)
83
+ }));
84
+ var verifications = pgTable("verifications", {
85
+ id: uuid("id").primaryKey().defaultRandom(),
86
+ identifier: text("identifier").notNull(),
87
+ // email or other identifier
88
+ value: text("value").notNull(),
89
+ // verification token
90
+ expiresAt: timestamp("expires_at").notNull(),
91
+ createdAt: timestamp("created_at").notNull().defaultNow(),
92
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
93
+ }, (table) => ({
94
+ identifierIdx: index("verifications_identifier_idx").on(table.identifier)
95
+ }));
96
+ var runnerKeys = pgTable("runner_keys", {
97
+ id: uuid("id").primaryKey().defaultRandom(),
98
+ userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
99
+ name: text("name").notNull(),
100
+ // User-friendly name like "My MacBook"
101
+ keyHash: text("key_hash").notNull(),
102
+ // SHA-256 hash of the full key
103
+ keyPrefix: text("key_prefix").notNull(),
104
+ // First 8 chars for display: "sv_abc123..."
105
+ source: text("source").default("web"),
106
+ // 'web' | 'cli' - how the key was created
107
+ lastUsedAt: timestamp("last_used_at"),
108
+ createdAt: timestamp("created_at").notNull().defaultNow(),
109
+ revokedAt: timestamp("revoked_at")
110
+ // Soft delete - null means active
111
+ }, (table) => ({
112
+ userIdIdx: index("runner_keys_user_id_idx").on(table.userId),
113
+ keyHashIdx: uniqueIndex("runner_keys_key_hash_idx").on(table.keyHash)
114
+ }));
115
+ var cliAuthSessions = pgTable("cli_auth_sessions", {
116
+ id: uuid("id").primaryKey().defaultRandom(),
117
+ token: text("token").notNull().unique(),
118
+ // Random token for session identification
119
+ callbackPort: integer("callback_port").notNull(),
120
+ // Port the CLI is listening on
121
+ callbackHost: text("callback_host").default("localhost"),
122
+ // Host for callback
123
+ state: text("state").notNull().default("pending"),
124
+ // 'pending' | 'authenticated' | 'completed' | 'expired'
125
+ userId: uuid("user_id").references(() => users.id, { onDelete: "cascade" }),
126
+ // Set after auth
127
+ runnerKeyId: uuid("runner_key_id").references(() => runnerKeys.id, { onDelete: "cascade" }),
128
+ // Created key
129
+ deviceName: text("device_name"),
130
+ // Auto-detected device name
131
+ expiresAt: timestamp("expires_at").notNull(),
132
+ // Session expiration (short-lived)
133
+ createdAt: timestamp("created_at").notNull().defaultNow(),
134
+ authenticatedAt: timestamp("authenticated_at")
135
+ // When user completed OAuth
136
+ }, (table) => ({
137
+ tokenIdx: uniqueIndex("cli_auth_sessions_token_idx").on(table.token),
138
+ expiresAtIdx: index("cli_auth_sessions_expires_at_idx").on(table.expiresAt)
139
+ }));
140
+ var projects = pgTable("projects", {
141
+ id: uuid("id").primaryKey().defaultRandom(),
142
+ userId: uuid("user_id").references(() => users.id, { onDelete: "set null" }),
143
+ // Owner of the project (nullable for migration/local mode)
144
+ name: text("name").notNull(),
145
+ slug: text("slug").notNull().unique(),
146
+ description: text("description"),
147
+ originalPrompt: text("original_prompt"),
148
+ icon: text("icon").default("Folder"),
149
+ status: text("status").notNull().default("pending"),
150
+ projectType: text("project_type"),
151
+ detectedFramework: text("detected_framework"),
152
+ // Auto-detected framework (astro, next, vite, etc.)
153
+ path: text("path"),
154
+ // Nullable - deprecated, path should be calculated from slug
155
+ runCommand: text("run_command"),
156
+ port: integer("port"),
157
+ devServerPid: integer("dev_server_pid"),
158
+ devServerPort: integer("dev_server_port"),
159
+ devServerStatus: text("dev_server_status").default("stopped"),
160
+ devServerStatusUpdatedAt: timestamp("dev_server_status_updated_at").defaultNow(),
161
+ tunnelUrl: text("tunnel_url"),
162
+ runnerId: text("runner_id"),
163
+ // Runner that created/manages this project
164
+ generationState: text("generation_state"),
165
+ designPreferences: jsonb("design_preferences"),
166
+ // User-specified design constraints (deprecated - use tags)
167
+ tags: jsonb("tags"),
168
+ // Tag-based configuration system
169
+ lastActivityAt: timestamp("last_activity_at").defaultNow(),
170
+ errorMessage: text("error_message"),
171
+ // GitHub integration fields
172
+ githubRepo: text("github_repo"),
173
+ // e.g., "owner/repo-name"
174
+ githubUrl: text("github_url"),
175
+ // Full repository URL
176
+ githubBranch: text("github_branch"),
177
+ // Default branch (e.g., "main")
178
+ githubLastPushedAt: timestamp("github_last_pushed_at"),
179
+ // Last push timestamp
180
+ githubAutoPush: boolean("github_auto_push").default(false),
181
+ // Auto-push after builds
182
+ githubLastSyncAt: timestamp("github_last_sync_at"),
183
+ // Last time we synced repo info
184
+ githubMeta: jsonb("github_meta"),
185
+ // Additional metadata (issues count, recent commits, etc.)
186
+ // NeonDB integration fields
187
+ neondbConnectionString: text("neondb_connection_string"),
188
+ // DATABASE_URL (encrypted/partial)
189
+ neondbClaimUrl: text("neondb_claim_url"),
190
+ // URL to claim the database
191
+ neondbHost: text("neondb_host"),
192
+ // Database host endpoint
193
+ neondbDatabase: text("neondb_database"),
194
+ // Database name
195
+ neondbCreatedAt: timestamp("neondb_created_at"),
196
+ // When database was provisioned
197
+ neondbExpiresAt: timestamp("neondb_expires_at"),
198
+ // When unclaimed DB expires (72 hours)
199
+ // Railway integration fields
200
+ railwayProjectId: text("railway_project_id"),
201
+ // Railway project ID
202
+ railwayServiceId: text("railway_service_id"),
203
+ // Railway service ID
204
+ railwayEnvironmentId: text("railway_environment_id"),
205
+ // Railway environment ID (production)
206
+ railwayDomain: text("railway_domain"),
207
+ // e.g., "myapp-production.up.railway.app"
208
+ railwayDeploymentStatus: text("railway_deployment_status"),
209
+ // 'deploying' | 'success' | 'failed' | 'crashed'
210
+ railwayLastDeployedAt: timestamp("railway_last_deployed_at"),
211
+ // Last successful deployment
212
+ createdAt: timestamp("created_at").notNull().defaultNow(),
213
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
214
+ }, (table) => ({
215
+ // Indexes for performance
216
+ userIdIdx: index("projects_user_id_idx").on(table.userId),
217
+ runnerIdIdx: index("projects_runner_id_idx").on(table.runnerId),
218
+ statusIdx: index("projects_status_idx").on(table.status),
219
+ lastActivityIdx: index("projects_last_activity_idx").on(table.lastActivityAt)
220
+ }));
221
+ var messages = pgTable("messages", {
222
+ id: uuid("id").primaryKey().defaultRandom(),
223
+ projectId: uuid("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
224
+ role: text("role").notNull(),
225
+ content: text("content").notNull(),
226
+ createdAt: timestamp("created_at").notNull().defaultNow()
227
+ });
228
+ var portAllocations = pgTable("port_allocations", {
229
+ port: integer("port").primaryKey(),
230
+ framework: text("framework").notNull(),
231
+ projectId: uuid("project_id").references(() => projects.id, { onDelete: "set null" }),
232
+ reservedAt: timestamp("reserved_at").defaultNow()
233
+ });
234
+ var runningProcesses = pgTable("running_processes", {
235
+ projectId: uuid("project_id").primaryKey().notNull().references(() => projects.id, { onDelete: "cascade" }),
236
+ pid: integer("pid").notNull(),
237
+ port: integer("port"),
238
+ command: text("command"),
239
+ runnerId: text("runner_id"),
240
+ // Runner that manages this process
241
+ startedAt: timestamp("started_at").notNull().defaultNow(),
242
+ lastHealthCheck: timestamp("last_health_check"),
243
+ healthCheckFailCount: integer("health_check_fail_count").notNull().default(0)
244
+ }, (table) => ({
245
+ // Index for filtering by runner
246
+ runnerIdIdx: index("running_processes_runner_id_idx").on(table.runnerId)
247
+ }));
248
+ var generationSessions = pgTable("generation_sessions", {
249
+ id: uuid("id").primaryKey().defaultRandom(),
250
+ projectId: uuid("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
251
+ buildId: text("build_id").notNull(),
252
+ operationType: text("operation_type"),
253
+ status: text("status").notNull().default("active"),
254
+ startedAt: timestamp("started_at").notNull().defaultNow(),
255
+ endedAt: timestamp("ended_at"),
256
+ summary: text("summary"),
257
+ rawState: jsonb("raw_state"),
258
+ isAutoFix: boolean("is_auto_fix").default(false),
259
+ // Flag for auto-fix sessions triggered by startup errors
260
+ autoFixError: text("auto_fix_error"),
261
+ // The error message that triggered the auto-fix
262
+ createdAt: timestamp("created_at").notNull().defaultNow(),
263
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
264
+ }, (table) => ({
265
+ projectIdIdx: index("generation_sessions_project_id_idx").on(table.projectId),
266
+ buildIdUnique: uniqueIndex("generation_sessions_build_id_unique").on(table.buildId)
267
+ }));
268
+ var generationTodos = pgTable("generation_todos", {
269
+ id: uuid("id").primaryKey().defaultRandom(),
270
+ sessionId: uuid("session_id").notNull().references(() => generationSessions.id, { onDelete: "cascade" }),
271
+ todoIndex: integer("todo_index").notNull(),
272
+ content: text("content").notNull(),
273
+ activeForm: text("active_form"),
274
+ status: text("status").notNull(),
275
+ createdAt: timestamp("created_at").notNull().defaultNow(),
276
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
277
+ }, (table) => ({
278
+ sessionIdIdx: index("generation_todos_session_id_idx").on(table.sessionId),
279
+ sessionIndexUnique: uniqueIndex("generation_todos_session_index_unique").on(table.sessionId, table.todoIndex)
280
+ }));
281
+ var generationToolCalls = pgTable("generation_tool_calls", {
282
+ id: uuid("id").primaryKey().defaultRandom(),
283
+ sessionId: uuid("session_id").notNull().references(() => generationSessions.id, { onDelete: "cascade" }),
284
+ todoIndex: integer("todo_index").notNull(),
285
+ toolCallId: text("tool_call_id").notNull(),
286
+ name: text("name").notNull(),
287
+ input: jsonb("input"),
288
+ output: jsonb("output"),
289
+ state: text("state").notNull(),
290
+ startedAt: timestamp("started_at").notNull().defaultNow(),
291
+ endedAt: timestamp("ended_at"),
292
+ createdAt: timestamp("created_at").notNull().defaultNow(),
293
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
294
+ }, (table) => ({
295
+ sessionIdIdx: index("generation_tool_calls_session_id_idx").on(table.sessionId),
296
+ toolCallUnique: uniqueIndex("generation_tool_calls_call_id_unique").on(table.sessionId, table.toolCallId)
297
+ }));
298
+ var generationNotes = pgTable("generation_notes", {
299
+ id: uuid("id").primaryKey().defaultRandom(),
300
+ sessionId: uuid("session_id").notNull().references(() => generationSessions.id, { onDelete: "cascade" }),
301
+ todoIndex: integer("todo_index").notNull(),
302
+ textId: text("text_id"),
303
+ kind: text("kind").notNull().default("text"),
304
+ content: text("content").notNull(),
305
+ createdAt: timestamp("created_at").notNull().defaultNow()
306
+ }, (table) => ({
307
+ sessionIdIdx: index("generation_notes_session_id_idx").on(table.sessionId),
308
+ textIdUnique: uniqueIndex("generation_notes_text_id_unique").on(table.sessionId, table.textId).where(sql`${table.textId} is not null`)
309
+ }));
310
+ var railwayConnections = pgTable("railway_connections", {
311
+ id: uuid("id").primaryKey().defaultRandom(),
312
+ userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
313
+ // Encrypted OAuth tokens
314
+ accessTokenEncrypted: text("access_token_encrypted").notNull(),
315
+ refreshTokenEncrypted: text("refresh_token_encrypted"),
316
+ accessTokenExpiresAt: timestamp("access_token_expires_at"),
317
+ // Railway user info (from OAuth)
318
+ railwayUserId: text("railway_user_id").notNull(),
319
+ // "sub" claim from OIDC
320
+ railwayEmail: text("railway_email"),
321
+ railwayName: text("railway_name"),
322
+ // Selected workspaces from OAuth consent
323
+ defaultWorkspaceId: text("default_workspace_id"),
324
+ defaultWorkspaceName: text("default_workspace_name"),
325
+ grantedWorkspaces: jsonb("granted_workspaces").$type(),
326
+ // Connection status
327
+ status: text("status").notNull().default("active"),
328
+ // 'active' | 'disconnected' | 'expired'
329
+ createdAt: timestamp("created_at").notNull().defaultNow(),
330
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
331
+ }, (table) => ({
332
+ userIdUnique: uniqueIndex("railway_connections_user_id_unique").on(table.userId),
333
+ railwayUserIdIdx: index("railway_connections_railway_user_id_idx").on(table.railwayUserId)
334
+ }));
335
+ var railwayDeployments = pgTable("railway_deployments", {
336
+ id: uuid("id").primaryKey().defaultRandom(),
337
+ projectId: uuid("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
338
+ // Railway resource IDs
339
+ railwayProjectId: text("railway_project_id").notNull(),
340
+ railwayServiceId: text("railway_service_id").notNull(),
341
+ railwayDeploymentId: text("railway_deployment_id").notNull(),
342
+ railwayEnvironmentId: text("railway_environment_id"),
343
+ // Deployment info
344
+ status: text("status").notNull(),
345
+ // Railway deployment status
346
+ url: text("url"),
347
+ // Deployment URL
348
+ commitSha: text("commit_sha"),
349
+ // Git commit deployed
350
+ // Timestamps
351
+ deployedAt: timestamp("deployed_at").notNull().defaultNow(),
352
+ completedAt: timestamp("completed_at"),
353
+ createdAt: timestamp("created_at").notNull().defaultNow()
354
+ }, (table) => ({
355
+ projectIdIdx: index("railway_deployments_project_id_idx").on(table.projectId),
356
+ railwayDeploymentIdIdx: index("railway_deployments_railway_deployment_id_idx").on(table.railwayDeploymentId)
357
+ }));
358
+ var githubConnections = pgTable("github_connections", {
359
+ id: uuid("id").primaryKey().defaultRandom(),
360
+ userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
361
+ // Encrypted OAuth tokens
362
+ accessTokenEncrypted: text("access_token_encrypted").notNull(),
363
+ // GitHub user info (from OAuth)
364
+ githubUserId: text("github_user_id").notNull(),
365
+ // GitHub user ID
366
+ githubUsername: text("github_username").notNull(),
367
+ // GitHub username/login
368
+ githubEmail: text("github_email"),
369
+ githubAvatarUrl: text("github_avatar_url"),
370
+ // OAuth scopes granted
371
+ scopes: text("scopes"),
372
+ // Comma-separated list of scopes
373
+ // Connection status
374
+ status: text("status").notNull().default("active"),
375
+ // 'active' | 'disconnected' | 'expired'
376
+ createdAt: timestamp("created_at").notNull().defaultNow(),
377
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
378
+ }, (table) => ({
379
+ userIdUnique: uniqueIndex("github_connections_user_id_unique").on(table.userId),
380
+ githubUserIdIdx: index("github_connections_github_user_id_idx").on(table.githubUserId)
381
+ }));
382
+ var serverOperations = pgTable("server_operations", {
383
+ id: uuid("id").primaryKey().defaultRandom(),
384
+ projectId: uuid("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
385
+ operation: text("operation").notNull(),
386
+ // 'start', 'stop', 'restart'
387
+ status: text("status").notNull().default("pending"),
388
+ // 'pending', 'sent', 'ack', 'completed', 'failed', 'timeout'
389
+ runnerId: text("runner_id"),
390
+ port: integer("port"),
391
+ pid: integer("pid"),
392
+ error: text("error"),
393
+ failureReason: text("failure_reason"),
394
+ // 'port_in_use', 'health_check_timeout', 'immediate_crash', etc.
395
+ retryCount: integer("retry_count").notNull().default(0),
396
+ metadata: jsonb("metadata"),
397
+ createdAt: timestamp("created_at").notNull().defaultNow(),
398
+ sentAt: timestamp("sent_at"),
399
+ ackAt: timestamp("ack_at"),
400
+ completedAt: timestamp("completed_at")
401
+ }, (table) => ({
402
+ projectIdIdx: index("server_operations_project_id_idx").on(table.projectId),
403
+ statusIdx: index("server_operations_status_idx").on(table.status),
404
+ createdAtIdx: index("server_operations_created_at_idx").on(table.createdAt)
405
+ }));
406
+
407
+ // src/lib/db/client.ts
408
+ var { Pool } = pg;
409
+ function createPostgresClient() {
410
+ const connectionString = process.env.DATABASE_URL;
411
+ if (!connectionString) {
412
+ throw new Error(
413
+ 'DATABASE_URL is not set. Please configure your database connection:\n - Run "hatchway init" to set up a Neon database\n - Or set DATABASE_URL environment variable to your PostgreSQL connection string'
414
+ );
415
+ }
416
+ const pool = new Pool({
417
+ connectionString,
418
+ ssl: process.env.PGSSLMODE === "disable" ? false : { rejectUnauthorized: false }
419
+ });
420
+ const client = drizzle(pool, { schema: schema_exports });
421
+ return client;
422
+ }
423
+ new Proxy({}, {
424
+ get(_target, prop) {
425
+ if (!global.__db) {
426
+ global.__db = createPostgresClient();
427
+ }
428
+ return global.__db[prop];
429
+ }
430
+ });
431
+ var BuildLogger = class {
432
+ buildId = null;
433
+ projectId = null;
434
+ /**
435
+ * Set correlation IDs for the current build
436
+ * Call this at the start of each build to enable correlation tracking
437
+ */
438
+ setBuildContext(buildId, projectId) {
439
+ this.buildId = buildId;
440
+ this.projectId = projectId;
441
+ this.log("debug", "runner", `Build context set: ${buildId} / ${projectId}`);
442
+ }
443
+ /**
444
+ * Clear correlation IDs after build completes
445
+ */
446
+ clearBuildContext() {
447
+ this.log("debug", "runner", "Build context cleared");
448
+ this.buildId = null;
449
+ this.projectId = null;
450
+ }
451
+ /**
452
+ * Core logging method - creates structured log entries
453
+ * Public for custom logging needs
454
+ */
455
+ log(level, context, message, data) {
456
+ if (process.env.SILENT_MODE === "1") {
457
+ return;
458
+ }
459
+ if (level === "info" || level === "debug") {
460
+ if (process.env.DEBUG_BUILD === "0") {
461
+ return;
462
+ }
463
+ }
464
+ ({
465
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
466
+ buildId: this.buildId ?? void 0,
467
+ projectId: this.projectId ?? void 0});
468
+ const prefix = `[${context}]`;
469
+ const icon = {
470
+ debug: "\u{1F50D}",
471
+ info: "\u{1F4CB}",
472
+ warn: "\u26A0\uFE0F ",
473
+ error: "\u274C"
474
+ }[level];
475
+ const logFn = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
476
+ if (data && Object.keys(data).length > 0) {
477
+ logFn(`${icon} ${prefix} ${message}`, data);
478
+ } else {
479
+ logFn(`${icon} ${prefix} ${message}`);
480
+ }
481
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "test") {
482
+ try {
483
+ if (level === "warn" || level === "error") {
484
+ Sentry.addBreadcrumb({
485
+ category: `build-logger.${context}`,
486
+ message,
487
+ level: level === "error" ? "error" : "warning",
488
+ data: {
489
+ ...data,
490
+ buildId: this.buildId,
491
+ projectId: this.projectId
492
+ }
493
+ });
494
+ }
495
+ } catch {
496
+ }
497
+ }
498
+ }
499
+ /**
500
+ * Orchestrator-specific logging methods
501
+ */
502
+ orchestrator = {
503
+ newProject: (operationType) => this.log("info", "orchestrator", `NEW PROJECT (operationType: ${operationType})`),
504
+ existingProject: (operationType) => this.log("info", "orchestrator", `EXISTING PROJECT (operationType: ${operationType})`),
505
+ templateProvided: (templateName, templateId, framework) => this.log("info", "orchestrator", `Frontend provided template: ${templateName}`, {
506
+ templateId,
507
+ framework
508
+ }),
509
+ templateSelecting: (method) => this.log("info", "orchestrator", `Template selection: ${method}`),
510
+ templateSelected: (templateName, templateId) => this.log("info", "orchestrator", `Selected template: ${templateName}`, { templateId }),
511
+ templateDownloading: (templateName, repository, target) => this.log("info", "orchestrator", `Downloading template: ${templateName}`, {
512
+ repository,
513
+ target
514
+ }),
515
+ templateDownloaded: (templateName, path, fileTreeSize) => this.log("info", "orchestrator", `Template downloaded: ${templateName}`, {
516
+ path,
517
+ fileTreeSize
518
+ }),
519
+ catalogPrepared: (catalogSize) => this.log("info", "orchestrator", `Template catalog prepared (${catalogSize} chars)`, {
520
+ catalogSize
521
+ }),
522
+ systemPromptGenerated: (size) => this.log("info", "orchestrator", `System prompt generated (${size} chars)`, { size }),
523
+ orchestrationComplete: (data) => this.log("info", "orchestrator", "Orchestration complete", data),
524
+ error: (message, error) => this.log("error", "orchestrator", message, {
525
+ error: error instanceof Error ? error.message : String(error)
526
+ })
527
+ };
528
+ /**
529
+ * Message transformer-specific logging methods
530
+ */
531
+ transformer = {
532
+ todoListFound: () => this.log("debug", "transformer", "Found Codex task list, parsing..."),
533
+ todoListParsed: (todoCount, completed, inProgress, pending) => this.log("info", "transformer", `Parsed ${todoCount} todos`, {
534
+ completed,
535
+ inProgress,
536
+ pending
537
+ }),
538
+ todoListInvalidFormat: (expected, got) => this.log("error", "transformer", "Invalid todo format from Codex", {
539
+ expected,
540
+ got: JSON.stringify(got).substring(0, 200)
541
+ }),
542
+ todoListParseError: (error, rawJson) => this.log("error", "transformer", "Failed to parse Codex todolist", {
543
+ error: String(error),
544
+ rawJson: rawJson.substring(0, 300)
545
+ }),
546
+ todoListRemoved: () => this.log("debug", "transformer", "Removed task list from chat text"),
547
+ toolStarted: (toolName, toolId) => this.log("debug", "transformer", `Tool started: ${toolName}`, { toolName, toolId }),
548
+ toolCompleted: (toolName, toolId) => this.log("debug", "transformer", `Tool completed: ${toolName}`, { toolName, toolId }),
549
+ pathViolationWarning: (toolName, path, workspace) => this.log("warn", "transformer", `Path outside workspace: ${path}`, {
550
+ toolName,
551
+ path,
552
+ workspace
553
+ }),
554
+ desktopPathDetected: (path) => this.log("error", "transformer", `DESKTOP PATH DETECTED - Likely hallucinated: ${path}`, { path })
555
+ };
556
+ /**
557
+ * Codex query-specific logging methods
558
+ */
559
+ codexQuery = {
560
+ promptBuilding: (workingDirectory, systemPromptSize, userPromptSize) => this.log("info", "codex-query", "Building Codex prompt", {
561
+ workingDirectory,
562
+ systemPromptSize,
563
+ userPromptSize
564
+ }),
565
+ threadStarting: () => this.log("info", "codex-query", "Starting Codex thread (multi-turn)"),
566
+ turnStarted: (turnNumber, maxTurns, promptSize) => this.log("info", "codex-query", `\u2550\u2550\u2550 Turn ${turnNumber}/${maxTurns} \u2550\u2550\u2550`, {
567
+ turnNumber,
568
+ maxTurns,
569
+ promptSize
570
+ }),
571
+ taskListExtracted: () => this.log("info", "codex-query", "Task list extracted and updated"),
572
+ taskListStatus: (completed, inProgress, pending, total) => this.log("info", "codex-query", `Tasks: ${completed} completed | ${inProgress} in_progress | ${pending} pending (total: ${total})`, {
573
+ completed,
574
+ inProgress,
575
+ pending,
576
+ total
577
+ }),
578
+ taskListTask: (index2, content, status, icon) => this.log("debug", "codex-query", ` ${icon} ${index2 + 1}. ${content}`, { status }),
579
+ taskListParseError: (error, rawContent) => this.log("error", "codex-query", "PARSE ERROR: Could not parse task list JSON", {
580
+ error: String(error),
581
+ rawContent: rawContent.substring(0, 200)
582
+ }),
583
+ taskListMissing: (turnNumber) => this.log("warn", "codex-query", `WARNING: No <start-todolist> tags found in Turn ${turnNumber}`, {
584
+ turnNumber
585
+ }),
586
+ turnComplete: (turnNumber, hadToolCalls, messageLength) => this.log("info", "codex-query", `Turn ${turnNumber} complete`, {
587
+ hadToolCalls,
588
+ messageLength
589
+ }),
590
+ tasksComplete: (completed, total) => this.log("info", "codex-query", `Task status: ${completed}/${total} completed`, {
591
+ completed,
592
+ total
593
+ }),
594
+ allComplete: () => this.log("info", "codex-query", "\u2705 All MVP tasks complete!"),
595
+ allTasksComplete: () => this.log("info", "codex-query", "\u2705 All MVP tasks complete!"),
596
+ taskCompleteDetected: () => this.log("info", "codex-query", "\u2705 Task complete (detected completion signal)"),
597
+ continuePrompting: (reason) => this.log("warn", "codex-query", `No tools used but not done - ${reason}`),
598
+ continuing: () => this.log("info", "codex-query", "\u23ED\uFE0F Continuing to next turn (had tool calls)"),
599
+ loopExited: (turnCount, maxTurns) => this.log("info", "codex-query", `EXITED WHILE LOOP after ${turnCount} turns`, {
600
+ turnCount,
601
+ maxTurns
602
+ }),
603
+ sessionComplete: (turnCount) => this.log("info", "codex-query", `Session complete after ${turnCount} turns`, { turnCount }),
604
+ error: (message, error) => this.log("error", "codex-query", message, {
605
+ error: error instanceof Error ? error.message : String(error)
606
+ })
607
+ };
608
+ /**
609
+ * Claude query-specific logging methods
610
+ */
611
+ claudeQuery = {
612
+ queryStarted: (model, cwd, maxTurns) => this.log("info", "claude-query", `Starting Claude query (${model})`, {
613
+ cwd,
614
+ maxTurns
615
+ }),
616
+ error: (message, error) => this.log("error", "claude-query", message, {
617
+ error: error instanceof Error ? error.message : String(error)
618
+ })
619
+ };
620
+ /**
621
+ * Runner-specific logging methods
622
+ */
623
+ runner = {
624
+ workspaceRoot: (path) => this.log("info", "runner", `Workspace root: ${path}`, { path }),
625
+ commandReceived: (commandType, projectId) => this.log("info", "runner", `Received command: ${commandType}`, { commandType, projectId }),
626
+ buildOperation: (operationType, projectSlug, agentId) => this.log("info", "runner", `Build operation: ${operationType}`, {
627
+ operationType,
628
+ projectSlug,
629
+ agentId
630
+ }),
631
+ templateProvided: (templateId) => this.log("info", "runner", `Template provided by frontend: ${templateId}`, { templateId }),
632
+ buildStreamCreated: () => this.log("info", "runner", "Build stream created, starting to process chunks..."),
633
+ firstChunkReceived: (agentLabel) => this.log("info", "runner", `First chunk received from ${agentLabel}`, { agentLabel }),
634
+ streamEnded: (chunkCount) => this.log("info", "runner", `Stream ended after ${chunkCount} chunks`, { chunkCount }),
635
+ buildCompleted: (projectId) => this.log("info", "runner", `Build completed successfully`, { projectId }),
636
+ buildFailed: (error) => this.log("error", "runner", `Build failed: ${error}`, { error }),
637
+ portDetected: (port) => this.log("info", "runner", `Port detected: ${port}`, { port }),
638
+ tunnelCreated: (port, tunnelUrl) => this.log("info", "runner", `Tunnel created: ${tunnelUrl} \u2192 localhost:${port}`, {
639
+ port,
640
+ tunnelUrl
641
+ }),
642
+ error: (message, error, context) => this.log("error", "runner", message, {
643
+ error: error instanceof Error ? error.message : String(error),
644
+ stack: error instanceof Error ? error.stack : void 0,
645
+ ...context
646
+ })
647
+ };
648
+ /**
649
+ * Build stream-specific logging (tool calls, text, etc.)
650
+ */
651
+ build = {
652
+ agentText: (agentLabel, text2) => {
653
+ const truncated = text2.length > 200 ? text2.slice(0, 200) + "..." : text2;
654
+ this.log("debug", "build", `${agentLabel}: ${truncated}`, {
655
+ agentLabel,
656
+ textLength: text2.length
657
+ });
658
+ },
659
+ agentThinking: (thinking) => {
660
+ const truncated = thinking.length > 300 ? thinking.slice(0, 300) + "..." : thinking;
661
+ this.log("debug", "build", `Thinking: ${truncated}`, {
662
+ thinkingLength: thinking.length
663
+ });
664
+ },
665
+ toolCalled: (toolName, toolId, inputSize) => this.log("info", "build", `Tool called: ${toolName} (${toolId})`, {
666
+ toolName,
667
+ toolId,
668
+ inputSize
669
+ }),
670
+ toolResult: (toolId, outputSize, isError) => this.log(isError ? "error" : "info", "build", `Tool result (${toolId})`, {
671
+ toolId,
672
+ outputSize,
673
+ isError
674
+ }),
675
+ runCommandDetected: (runCommand) => this.log("info", "build", `Detected runCommand: ${runCommand}`, { runCommand })
676
+ };
677
+ /**
678
+ * WebSocket-specific logging methods
679
+ */
680
+ websocket = {
681
+ serverCreated: (instanceId) => this.log("info", "websocket", `WebSocket server instance created`, { instanceId }),
682
+ serverInitialized: (path, runnerPath) => this.log("info", "websocket", `Server initialized`, { path, runnerPath }),
683
+ clientConnected: (clientId, projectId, sessionId) => this.log("info", "websocket", `Client connected: ${clientId}`, { clientId, projectId, sessionId }),
684
+ clientDisconnected: (clientId) => this.log("info", "websocket", `Client disconnected: ${clientId}`, { clientId }),
685
+ clientSubscribed: (clientId, projectId) => this.log("info", "websocket", `Client subscribed to project: ${projectId}`, { clientId, projectId }),
686
+ clientTimeout: (clientId) => this.log("warn", "websocket", `Client timeout: ${clientId}`, { clientId }),
687
+ runnerConnected: (runnerId) => this.log("info", "websocket", `Runner connected: ${runnerId}`, { runnerId }),
688
+ runnerDisconnected: (runnerId, code) => this.log("info", "websocket", `Runner disconnected: ${runnerId}`, { runnerId, code }),
689
+ runnerNotConnected: (runnerId, commandType) => this.log("warn", "websocket", `Cannot send command to runner ${runnerId}: not connected`, {
690
+ runnerId,
691
+ commandType
692
+ }),
693
+ runnerAuthRejected: () => this.log("warn", "websocket", `Runner connection rejected: invalid auth`),
694
+ runnerAuthMissing: () => this.log("error", "websocket", `RUNNER_SHARED_SECRET is not configured`),
695
+ runnerStaleRemoved: (runnerId) => this.log("info", "websocket", `Removing stale runner connection: ${runnerId}`, { runnerId }),
696
+ commandSent: (runnerId, commandType, traceAttached) => this.log("debug", "websocket", `Sent command to runner: ${commandType}`, {
697
+ runnerId,
698
+ commandType,
699
+ traceAttached
700
+ }),
701
+ eventReceived: (runnerId, eventType) => this.log("debug", "websocket", `Received event from runner: ${eventType}`, {
702
+ runnerId,
703
+ eventType
704
+ }),
705
+ broadcastToolCall: (toolName, toolState, subscriberCount) => this.log("info", "websocket", `Broadcasting planning tool: ${toolName} (state=${toolState})`, {
706
+ toolName,
707
+ toolState,
708
+ subscriberCount
709
+ }),
710
+ broadcastBuildComplete: (projectId, sessionId, subscriberCount) => this.log("info", "websocket", `Broadcasting build-complete`, {
711
+ projectId,
712
+ sessionId,
713
+ subscriberCount
714
+ }),
715
+ unknownUpgradePath: (pathname) => this.log("warn", "websocket", `Unknown upgrade path: ${pathname}`, { pathname }),
716
+ shutdown: () => this.log("info", "websocket", `Shutting down server...`),
717
+ shutdownComplete: () => this.log("info", "websocket", `Server shut down`),
718
+ error: (message, error, context) => this.log("error", "websocket", message, {
719
+ error: error instanceof Error ? error.message : String(error),
720
+ stack: error instanceof Error ? error.stack : void 0,
721
+ ...context
722
+ })
723
+ };
724
+ /**
725
+ * Port allocator-specific logging methods
726
+ */
727
+ portAllocator = {
728
+ portAllocated: (port, projectId) => this.log("info", "port-allocator", `Port allocated: ${port}`, { port, projectId }),
729
+ portReleased: (port, projectId) => this.log("info", "port-allocator", `Port released: ${port}`, { port, projectId }),
730
+ portInUse: (port) => this.log("warn", "port-allocator", `Port ${port} is already in use`, { port }),
731
+ portRangeExhausted: (minPort, maxPort) => this.log("error", "port-allocator", `No available ports in range ${minPort}-${maxPort}`, {
732
+ minPort,
733
+ maxPort
734
+ }),
735
+ portConflict: (port, projectId, existingProjectId) => this.log("warn", "port-allocator", `Port ${port} conflict detected`, {
736
+ port,
737
+ projectId,
738
+ existingProjectId
739
+ }),
740
+ allocationsCleared: (count) => this.log("info", "port-allocator", `Cleared ${count} port allocations`, { count }),
741
+ error: (message, error, context) => this.log("error", "port-allocator", message, {
742
+ error: error instanceof Error ? error.message : String(error),
743
+ ...context
744
+ })
745
+ };
746
+ /**
747
+ * Process manager-specific logging methods
748
+ */
749
+ processManager = {
750
+ processStarting: (projectId, command, cwd) => this.log("info", "process-manager", `Starting process: ${command}`, {
751
+ projectId,
752
+ command,
753
+ cwd
754
+ }),
755
+ processStarted: (projectId, pid) => this.log("info", "process-manager", `Process started`, { projectId, pid }),
756
+ processOutput: (projectId, output) => this.log("debug", "process-manager", `Process output: ${output.substring(0, 100)}`, {
757
+ projectId,
758
+ outputLength: output.length
759
+ }),
760
+ processError: (projectId, error) => this.log("error", "process-manager", `Process error: ${error}`, { projectId, error }),
761
+ processExited: (projectId, code, signal) => this.log("info", "process-manager", `Process exited`, { projectId, code, signal }),
762
+ processStopped: (projectId) => this.log("info", "process-manager", `Process stopped`, { projectId }),
763
+ processNotFound: (projectId) => this.log("warn", "process-manager", `Process not found for project: ${projectId}`, { projectId }),
764
+ processKilled: (projectId, signal) => this.log("info", "process-manager", `Process killed with signal: ${signal}`, {
765
+ projectId,
766
+ signal
767
+ }),
768
+ processCleanup: (projectId) => this.log("info", "process-manager", `Cleaning up process`, { projectId }),
769
+ processListRetrieved: (count) => this.log("debug", "process-manager", `Retrieved ${count} running processes`, { count }),
770
+ error: (message, error, context) => this.log("error", "process-manager", message, {
771
+ error: error instanceof Error ? error.message : String(error),
772
+ stack: error instanceof Error ? error.stack : void 0,
773
+ ...context
774
+ })
775
+ };
776
+ /**
777
+ * Build events-specific logging methods
778
+ */
779
+ buildEvents = {
780
+ eventReceived: (eventType, projectId, sessionId) => this.log("info", "build-events", `Received event: ${eventType}`, {
781
+ eventType,
782
+ projectId,
783
+ sessionId
784
+ }),
785
+ eventProcessed: (eventType, projectId) => this.log("debug", "build-events", `Processed event: ${eventType}`, { eventType, projectId }),
786
+ buildStarted: (projectId, sessionId) => this.log("info", "build-events", `Build started`, { projectId, sessionId }),
787
+ buildCompleted: (projectId, sessionId, success) => this.log("info", "build-events", `Build ${success ? "completed" : "failed"}`, {
788
+ projectId,
789
+ sessionId,
790
+ success
791
+ }),
792
+ portDetected: (projectId, port) => this.log("info", "build-events", `Port detected: ${port}`, { projectId, port }),
793
+ devServerStarted: (projectId, port, url) => this.log("info", "build-events", `Dev server started: ${url}`, { projectId, port, url }),
794
+ devServerError: (projectId, error) => this.log("error", "build-events", `Dev server error: ${error}`, { projectId, error }),
795
+ toolCallReceived: (toolName, toolId) => this.log("debug", "build-events", `Tool call: ${toolName}`, { toolName, toolId }),
796
+ logChunkReceived: (projectId, chunkSize) => this.log("debug", "build-events", `Log chunk received`, { projectId, chunkSize }),
797
+ invalidEvent: (reason) => this.log("warn", "build-events", `Invalid event: ${reason}`, { reason }),
798
+ error: (message, error, context) => this.log("error", "build-events", message, {
799
+ error: error instanceof Error ? error.message : String(error),
800
+ stack: error instanceof Error ? error.stack : void 0,
801
+ ...context
802
+ })
803
+ };
804
+ };
805
+ var buildLogger = new BuildLogger();
806
+ async function detectFrameworkFromFilesystem(projectPath) {
807
+ try {
808
+ let cachedPkg = null;
809
+ const loadPackageJson = async () => {
810
+ if (cachedPkg) return cachedPkg;
811
+ const pkgPath = join(projectPath, "package.json");
812
+ if (!existsSync(pkgPath)) {
813
+ cachedPkg = null;
814
+ return cachedPkg;
815
+ }
816
+ const pkgContent = await readFile(pkgPath, "utf-8");
817
+ const pkg2 = JSON.parse(pkgContent);
818
+ cachedPkg = {
819
+ deps: { ...pkg2.dependencies ?? {}, ...pkg2.peerDependencies ?? {} },
820
+ devDeps: pkg2.devDependencies ?? {},
821
+ devScript: pkg2.scripts?.dev?.toLowerCase() ?? ""
822
+ };
823
+ return cachedPkg;
824
+ };
825
+ const hasTanStackDependency = async () => {
826
+ const pkg2 = await loadPackageJson();
827
+ if (!pkg2) return false;
828
+ const combined = { ...pkg2.deps, ...pkg2.devDeps };
829
+ if (combined["@tanstack/react-start"]) return true;
830
+ if (pkg2.devScript.includes("tanstack")) return true;
831
+ return false;
832
+ };
833
+ if (existsSync(join(projectPath, "astro.config.mjs")) || existsSync(join(projectPath, "astro.config.ts")) || existsSync(join(projectPath, "astro.config.js"))) {
834
+ return "astro";
835
+ }
836
+ if (existsSync(join(projectPath, "next.config.ts")) || existsSync(join(projectPath, "next.config.js")) || existsSync(join(projectPath, "next.config.mjs"))) {
837
+ return "next";
838
+ }
839
+ if (existsSync(join(projectPath, "vite.config.ts")) || existsSync(join(projectPath, "vite.config.js"))) {
840
+ if (await hasTanStackDependency()) {
841
+ return "tanstack";
842
+ }
843
+ return "vite";
844
+ }
845
+ const pkg = await loadPackageJson();
846
+ if (pkg) {
847
+ const allDeps = { ...pkg.deps, ...pkg.devDeps };
848
+ if (allDeps["astro"]) return "astro";
849
+ if (allDeps["@tanstack/react-start"]) return "tanstack";
850
+ if (allDeps["next"]) return "next";
851
+ if (allDeps["vite"]) return "vite";
852
+ if (pkg.devScript.includes("tanstack")) return "tanstack";
853
+ if (pkg.devScript.includes("astro")) return "astro";
854
+ if (pkg.devScript.includes("next")) return "next";
855
+ if (pkg.devScript.includes("vite")) return "vite";
856
+ }
857
+ } catch (error) {
858
+ buildLogger.portAllocator.error("Failed to detect framework from filesystem", error);
859
+ }
860
+ return null;
861
+ }
862
+
863
+ export { detectFrameworkFromFilesystem };
864
+ //# sourceMappingURL=port-allocator-BENntRMG.js.map