@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.
- package/README.md +274 -0
- package/bin/hatchway.js +31 -0
- package/dist/chunks/Banner-DL1Fpz_g.js +115 -0
- package/dist/chunks/Banner-DL1Fpz_g.js.map +1 -0
- package/dist/chunks/auto-update-Ddo5Ntt7.js +264 -0
- package/dist/chunks/auto-update-Ddo5Ntt7.js.map +1 -0
- package/dist/chunks/build-V8_D-JHF.js +116 -0
- package/dist/chunks/build-V8_D-JHF.js.map +1 -0
- package/dist/chunks/cleanup-BNuJNSve.js +141 -0
- package/dist/chunks/cleanup-BNuJNSve.js.map +1 -0
- package/dist/chunks/cli-auth-B4Do-N8Y.js +340 -0
- package/dist/chunks/cli-auth-B4Do-N8Y.js.map +1 -0
- package/dist/chunks/cli-error-1drkrXNn.js +140 -0
- package/dist/chunks/cli-error-1drkrXNn.js.map +1 -0
- package/dist/chunks/config-hFJA7z5y.js +167 -0
- package/dist/chunks/config-hFJA7z5y.js.map +1 -0
- package/dist/chunks/config-manager-DST6RbP8.js +133 -0
- package/dist/chunks/config-manager-DST6RbP8.js.map +1 -0
- package/dist/chunks/database-YGb1Lzim.js +68 -0
- package/dist/chunks/database-YGb1Lzim.js.map +1 -0
- package/dist/chunks/database-setup-U31oEs90.js +253 -0
- package/dist/chunks/database-setup-U31oEs90.js.map +1 -0
- package/dist/chunks/devtools-CPruVlOo.js +75 -0
- package/dist/chunks/devtools-CPruVlOo.js.map +1 -0
- package/dist/chunks/index-DCC6HGdr.js +119 -0
- package/dist/chunks/index-DCC6HGdr.js.map +1 -0
- package/dist/chunks/init-DkXJVFFx.js +472 -0
- package/dist/chunks/init-DkXJVFFx.js.map +1 -0
- package/dist/chunks/init-tui-D2VOVdeK.js +1131 -0
- package/dist/chunks/init-tui-D2VOVdeK.js.map +1 -0
- package/dist/chunks/logger-6V5cBxba.js +38 -0
- package/dist/chunks/logger-6V5cBxba.js.map +1 -0
- package/dist/chunks/login-CA1XWUEM.js +63 -0
- package/dist/chunks/login-CA1XWUEM.js.map +1 -0
- package/dist/chunks/logout-BC4VFt8f.js +40 -0
- package/dist/chunks/logout-BC4VFt8f.js.map +1 -0
- package/dist/chunks/main-tui-D8KkJRd_.js +648 -0
- package/dist/chunks/main-tui-D8KkJRd_.js.map +1 -0
- package/dist/chunks/manager-DjVI7erc.js +1161 -0
- package/dist/chunks/manager-DjVI7erc.js.map +1 -0
- package/dist/chunks/port-allocator-BENntRMG.js +864 -0
- package/dist/chunks/port-allocator-BENntRMG.js.map +1 -0
- package/dist/chunks/process-killer-ChXAqhfm.js +87 -0
- package/dist/chunks/process-killer-ChXAqhfm.js.map +1 -0
- package/dist/chunks/prompts-Beijr8dm.js +128 -0
- package/dist/chunks/prompts-Beijr8dm.js.map +1 -0
- package/dist/chunks/repo-cloner-UY3L2X7h.js +219 -0
- package/dist/chunks/repo-cloner-UY3L2X7h.js.map +1 -0
- package/dist/chunks/repo-detector-36VydrlB.js +66 -0
- package/dist/chunks/repo-detector-36VydrlB.js.map +1 -0
- package/dist/chunks/run-Du6dvTJL.js +697 -0
- package/dist/chunks/run-Du6dvTJL.js.map +1 -0
- package/dist/chunks/runner-logger-instance-Dj_JMznn.js +899 -0
- package/dist/chunks/runner-logger-instance-Dj_JMznn.js.map +1 -0
- package/dist/chunks/spinner-DTH0QZQw.js +53 -0
- package/dist/chunks/spinner-DTH0QZQw.js.map +1 -0
- package/dist/chunks/start-Dkuro1jp.js +1713 -0
- package/dist/chunks/start-Dkuro1jp.js.map +1 -0
- package/dist/chunks/start-traditional-7wlD2f2H.js +255 -0
- package/dist/chunks/start-traditional-7wlD2f2H.js.map +1 -0
- package/dist/chunks/status-BU3cFJm1.js +97 -0
- package/dist/chunks/status-BU3cFJm1.js.map +1 -0
- package/dist/chunks/theme-NAQBkisB.js +40222 -0
- package/dist/chunks/theme-NAQBkisB.js.map +1 -0
- package/dist/chunks/upgrade-BBpJirEu.js +455 -0
- package/dist/chunks/upgrade-BBpJirEu.js.map +1 -0
- package/dist/chunks/use-app-Ct3w2jLI.js +10 -0
- package/dist/chunks/use-app-Ct3w2jLI.js.map +1 -0
- package/dist/chunks/useBuildState-Dy7pRR8Z.js +330 -0
- package/dist/chunks/useBuildState-Dy7pRR8Z.js.map +1 -0
- package/dist/cli/index.js +712 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.js +13625 -0
- package/dist/index.js.map +1 -0
- package/dist/instrument.js +45 -0
- package/dist/instrument.js.map +1 -0
- package/dist/templates.json +295 -0
- package/package.json +87 -0
- package/templates/config.template.json +18 -0
- 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
|