diff --git a/.changeset/whole-pumas-train.md b/.changeset/whole-pumas-train.md new file mode 100644 index 0000000000..2df799cd83 --- /dev/null +++ b/.changeset/whole-pumas-train.md @@ -0,0 +1,7 @@ +--- +"@workflow/web-shared": patch +"@workflow/errors": patch +"@workflow/cli": patch +--- + +Move auth error messages into @workflow/errors package diff --git a/packages/cli/src/commands/inspect.ts b/packages/cli/src/commands/inspect.ts index 85aa3dd95c..54d13ed640 100644 --- a/packages/cli/src/commands/inspect.ts +++ b/packages/cli/src/commands/inspect.ts @@ -1,4 +1,5 @@ import { Args, Flags } from '@oclif/core'; +import { VERCEL_403_ERROR_MESSAGE } from '@workflow/errors'; import { BaseCommand } from '../base.js'; import { LOGGING_CONFIG, logger } from '../lib/config/log.js'; import type { InspectCLIOptions } from '../lib/config/types.js'; @@ -35,8 +36,7 @@ export default class Inspect extends BaseCommand { async catch(error: any) { // Check if this is a 403 error from the Vercel backend if (error?.status === 403) { - const message = - 'Your current vercel account does not have access to this workflow run. Please use `vercel login` to login, or use `vercel switch` to ensure you can access the correct team.'; + const message = VERCEL_403_ERROR_MESSAGE; logger.error(message); } else if (LOGGING_CONFIG.VERBOSE_MODE) { logger.error(error); diff --git a/packages/cli/src/commands/web.ts b/packages/cli/src/commands/web.ts index 4fb10acd4a..a168e6abc0 100644 --- a/packages/cli/src/commands/web.ts +++ b/packages/cli/src/commands/web.ts @@ -1,4 +1,5 @@ import { Args } from '@oclif/core'; +import { VERCEL_403_ERROR_MESSAGE } from '@workflow/errors'; import { BaseCommand } from '../base.js'; import { LOGGING_CONFIG, logger } from '../lib/config/log.js'; import { cliFlags } from '../lib/inspect/flags.js'; @@ -19,8 +20,7 @@ export default class Web extends BaseCommand { async catch(error: any) { // Check if this is a 403 error from the Vercel backend if (error?.status === 403) { - const message = - 'Your current vercel account does not have access to this workflow run. Please use `vercel login` to login, or use `vercel switch` to ensure you can access the correct team.'; + const message = VERCEL_403_ERROR_MESSAGE; logger.error(message); } else if (LOGGING_CONFIG.VERBOSE_MODE) { logger.error(error); diff --git a/packages/cli/src/lib/inspect/output.ts b/packages/cli/src/lib/inspect/output.ts index 4c3ae98e13..e41540748b 100644 --- a/packages/cli/src/lib/inspect/output.ts +++ b/packages/cli/src/lib/inspect/output.ts @@ -4,6 +4,7 @@ import { getDeserializeStream, getExternalRevivers, } from '@workflow/core/serialization'; +import { VERCEL_403_ERROR_MESSAGE } from '@workflow/errors'; import type { Event, Hook, @@ -139,9 +140,7 @@ const checkAndHandleVercelAccessError = ( if (backend === 'vercel' && error && typeof error === 'object') { const err = error as Record; if (err.status === 403) { - logger.error( - 'Your current vercel account does not have access to this workflow run. Please use `vercel login` to login, or use `vercel switch` to ensure you can access the correct team.' - ); + logger.error(VERCEL_403_ERROR_MESSAGE); return true; } } diff --git a/packages/errors/src/index.ts b/packages/errors/src/index.ts index 7a82a89acc..071fc359d1 100644 --- a/packages/errors/src/index.ts +++ b/packages/errors/src/index.ts @@ -284,3 +284,6 @@ export class RetryableError extends Error { return isError(value) && value.name === 'RetryableError'; } } + +export const VERCEL_403_ERROR_MESSAGE = + 'Your current vercel account does not have access to this resource. Use `vercel login` or `vercel switch` to ensure you are linked to the right account. You might need to run `vercel env pull` to use the latest environment variables.'; diff --git a/packages/web-shared/src/api/workflow-api-client.ts b/packages/web-shared/src/api/workflow-api-client.ts index a9c1ab0489..c9e7833c60 100644 --- a/packages/web-shared/src/api/workflow-api-client.ts +++ b/packages/web-shared/src/api/workflow-api-client.ts @@ -1,5 +1,6 @@ 'use client'; +import { VERCEL_403_ERROR_MESSAGE } from '@workflow/errors'; import type { Event, Hook, @@ -55,7 +56,7 @@ export const getErrorMessage = (error: Error | WorkflowWebAPIError): string => { if ('layer' in error && error.layer) { if (error instanceof WorkflowWebAPIError) { if (error.request?.status === 403) { - return 'Your current Vercel account does not have access to this data. Please use `vercel login` to log in, or use `vercel switch` to ensure you can access the correct team.'; + return VERCEL_403_ERROR_MESSAGE; } } diff --git a/packages/web-shared/src/stream-viewer.tsx b/packages/web-shared/src/stream-viewer.tsx index bbb654f59f..876f9006f9 100644 --- a/packages/web-shared/src/stream-viewer.tsx +++ b/packages/web-shared/src/stream-viewer.tsx @@ -22,6 +22,7 @@ interface Chunk { export function StreamViewer({ env, streamId }: StreamViewerProps) { const [chunks, setChunks] = useState([]); const [isLive, setIsLive] = useState(true); + // TODO: Handle 410 error specifically (stream expired) const [error, setError] = useState(null); const [hasMoreBelow, setHasMoreBelow] = useState(false); const scrollRef = useRef(null); diff --git a/workbench/nextjs-turbopack/app/api/workflows/stream/route.ts b/workbench/nextjs-turbopack/app/api/workflows/stream/route.ts index d1d0545500..465d227441 100644 --- a/workbench/nextjs-turbopack/app/api/workflows/stream/route.ts +++ b/workbench/nextjs-turbopack/app/api/workflows/stream/route.ts @@ -21,27 +21,8 @@ export async function POST(request: NextRequest) { ); } - // Get the stream if the run has a stream method - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const runObj = run as any; - let stream: ReadableStream | null = null; - if (typeof runObj.stream === 'function') { - try { - stream = runObj.stream(); - } catch (error) { - console.error('Error calling run.stream():', error); - } - } - - if (!stream) { - return NextResponse.json( - { error: 'Stream not available for this run' }, - { status: 404 } - ); - } - - // Create a response with the stream - return new Response(stream, { + const readable = run.getReadable(); + return new Response(readable, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache',