diff --git a/apps/api/src/lib/errors.ts b/apps/api/src/lib/errors.ts deleted file mode 100644 index d730d757..00000000 --- a/apps/api/src/lib/errors.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { ApiError } from '@simple-agent-manager/shared'; -import type { Context } from 'hono'; - -/** - * Standard error codes - */ -export const ErrorCodes = { - UNAUTHORIZED: 'unauthorized', - FORBIDDEN: 'forbidden', - NOT_FOUND: 'not_found', - VALIDATION_ERROR: 'validation_error', - PROVIDER_ERROR: 'provider_error', - INTERNAL_ERROR: 'internal_error', - RATE_LIMIT_EXCEEDED: 'rate_limit_exceeded', - WORKSPACE_NOT_FOUND: 'workspace_not_found', - WORKSPACE_ALREADY_STOPPED: 'workspace_already_stopped', - WORKSPACE_NOT_RUNNING: 'workspace_not_running', - EXEC_NOT_SUPPORTED: 'exec_not_supported', - INVALID_REPO_URL: 'invalid_repo_url', - INVALID_SIZE: 'invalid_size', - // Note: INVALID_API_KEY removed - users authenticate via `claude login` - GITHUB_NOT_CONNECTED: 'github_not_connected', - REPO_NOT_ACCESSIBLE: 'repo_not_accessible', - PROVIDER_UNAVAILABLE: 'provider_unavailable', -} as const; - -export type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes]; - -/** - * Create a standard error response - */ -export function errorResponse( - c: Context, - status: number, - code: ErrorCode, - message: string, - details?: Record -) { - const body: ApiError = { - error: code, - message, - ...(details && { details }), - }; - return c.json(body, status as 400 | 401 | 403 | 404 | 409 | 429 | 500 | 502 | 503); -} - -/** - * 400 Bad Request - */ -export function badRequest(c: Context, code: ErrorCode, message: string) { - return errorResponse(c, 400, code, message); -} - -/** - * 401 Unauthorized - */ -export function unauthorized(c: Context, message = 'Unauthorized') { - return errorResponse(c, 401, ErrorCodes.UNAUTHORIZED, message); -} - -/** - * 404 Not Found - */ -export function notFound(c: Context, message = 'Resource not found') { - return errorResponse(c, 404, ErrorCodes.NOT_FOUND, message); -} - -/** - * 409 Conflict - */ -export function conflict(c: Context, code: ErrorCode, message: string) { - return errorResponse(c, 409, code, message); -} - -/** - * 500 Internal Server Error - */ -export function internalError(c: Context, message = 'Internal server error') { - return errorResponse(c, 500, ErrorCodes.INTERNAL_ERROR, message); -} - -/** - * 502 Bad Gateway (provider error) - */ -export function providerError(c: Context, message: string) { - return errorResponse(c, 502, ErrorCodes.PROVIDER_ERROR, message); -} - -/** - * 503 Service Unavailable - */ -export function serviceUnavailable(c: Context, message: string) { - return errorResponse(c, 503, ErrorCodes.PROVIDER_UNAVAILABLE, message); -} diff --git a/apps/api/src/routes/chat.ts b/apps/api/src/routes/chat.ts index 06148218..3ee5e5fb 100644 --- a/apps/api/src/routes/chat.ts +++ b/apps/api/src/routes/chat.ts @@ -15,6 +15,7 @@ import { Hono } from 'hono'; import * as schema from '../db/schema'; import type { Env } from '../env'; import { log } from '../lib/logger'; +import { requireRouteParam } from '../lib/route-helpers'; import { getUserId, requireApproved,requireAuth } from '../middleware/auth'; import { errors } from '../middleware/error'; import { requireOwnedProject } from '../middleware/project-auth'; @@ -25,17 +26,6 @@ import { isTaskStatus } from '../services/task-status'; const chatRoutes = new Hono<{ Bindings: Env }>(); -function requireRouteParam( - c: { req: { param: (name: string) => string | undefined } }, - name: string -): string { - const value = c.req.param(name); - if (!value) { - throw errors.badRequest(`${name} is required`); - } - return value; -} - chatRoutes.use('/*', requireAuth(), requireApproved()); /** diff --git a/apps/api/src/routes/nodes.ts b/apps/api/src/routes/nodes.ts index ecc69c16..cfe0fd9f 100644 --- a/apps/api/src/routes/nodes.ts +++ b/apps/api/src/routes/nodes.ts @@ -278,7 +278,7 @@ nodesRoutes.post('/:id/stop', async (c) => { .where(inArray(schema.agentSessions.workspaceId, workspaceIds)); } - return c.json({ status: 'deleted' }); + return c.json({ status: 'stopped' }); }); nodesRoutes.delete('/:id', async (c) => { diff --git a/apps/api/src/routes/tasks/_helpers.ts b/apps/api/src/routes/tasks/_helpers.ts index 5b047eb4..d857f01f 100644 --- a/apps/api/src/routes/tasks/_helpers.ts +++ b/apps/api/src/routes/tasks/_helpers.ts @@ -248,14 +248,16 @@ export async function setTaskStatus( errorMessage: toStatus === 'failed' ? (options.errorMessage?.trim() || 'Task failed') : null, }) .where(eq(schema.triggerExecutions.id, updatedTask.triggerExecutionId)) - .catch((err) => { + .catch((err: unknown) => { // Best-effort — don't fail the task status update if execution sync fails // eslint-disable-next-line no-console - console.error('trigger_execution_sync_failed', { + console.error(JSON.stringify({ + level: 'error', + event: 'trigger_execution_sync_failed', taskId: task.id, triggerExecutionId: updatedTask.triggerExecutionId, error: String(err), - }); + })); }); } diff --git a/apps/web/src/pages/CreateWorkspace.tsx b/apps/web/src/pages/CreateWorkspace.tsx index 35c350e1..37b90023 100644 --- a/apps/web/src/pages/CreateWorkspace.tsx +++ b/apps/web/src/pages/CreateWorkspace.tsx @@ -196,7 +196,7 @@ export function CreateWorkspace() { setBranchesError('Could not fetch branches, showing common defaults'); } } catch (err) { - console.log('Could not fetch branches:', err); + console.error('Could not fetch branches:', err); // Provide common branch names as fallback setBranches([{ name: 'main' }, { name: 'master' }, { name: 'develop' }]); setBranchesError('Unable to fetch branches. Common branch names provided.'); diff --git a/packages/providers/src/types.ts b/packages/providers/src/types.ts index dfb092a0..a09a6bd4 100644 --- a/packages/providers/src/types.ts +++ b/packages/providers/src/types.ts @@ -179,4 +179,15 @@ export class ProviderError extends Error { ) { super(message, options); } + + /** Make Error properties visible to JSON.stringify */ + toJSON(): Record { + return { + name: this.name, + message: this.message, + provider: this.providerName, + statusCode: this.statusCode, + cause: this.cause instanceof Error ? this.cause.message : this.cause, + }; + } } diff --git a/scripts/deploy/types.ts b/scripts/deploy/types.ts index bd542dbe..3615c1c9 100644 --- a/scripts/deploy/types.ts +++ b/scripts/deploy/types.ts @@ -125,18 +125,6 @@ export interface MigrationStatus { lastRun?: string; } -export interface DeploymentState { - version: string; - environment: string; - timestamp: string; - status: DeploymentStatus; - resources: ProvisionedResources; - dnsRecords: DnsRecord[]; - steps: DeploymentStep[]; - secretsConfigured: string[]; - migrations: MigrationStatus; -} - // ============================================================================ // Preflight Check Types // ============================================================================ @@ -314,8 +302,6 @@ export interface CloudflareWorker { // Constants // ============================================================================ -export const DEPLOYMENT_STATE_VERSION = '1.0.0'; - // Note: Resource naming is centralized in config.ts (DEPLOYMENT_CONFIG.resources) // Do NOT add resource naming constants here - use DEPLOYMENT_CONFIG instead.