MCPcopy Create free account
hub / github.com/ShipSecAI/studio / stream

Method stream

backend/src/workflows/workflows.controller.ts:863–1116  ·  view source on GitHub ↗
(
    @Param('runId') runId: string,
    @Query(new ZodValidationPipe(StreamRunQuerySchema)) query: StreamRunQueryDto,
    @CurrentAuth() auth: AuthContext | null,
    @Res() res: Response,
    @Req() req: Request,
  )

Source from the content-addressed store, hash-verified

861 @Get('/runs/:runId/stream')
862 @ApiOkResponse({ description: 'Server-sent events stream for workflow run updates' })
863 async stream(
864 @Param('runId') runId: string,
865 @Query(new ZodValidationPipe(StreamRunQuerySchema)) query: StreamRunQueryDto,
866 @CurrentAuth() auth: AuthContext | null,
867 @Res() res: Response,
868 @Req() req: Request,
869 ): Promise<void> {
870 // Auth is now handled via headers (Authorization and X-Organization-Id)
871 // using a fetch-based SSE client that supports custom headers
872 res.setHeader('Content-Type', 'text/event-stream');
873 res.setHeader('Cache-Control', 'no-cache');
874 res.setHeader('Connection', 'keep-alive');
875 if (typeof (res as any).flushHeaders === 'function') {
876 (res as any).flushHeaders();
877 }
878
879 await this.workflowsService.ensureRunAccess(runId, auth);
880
881 let lastSequence = Number.parseInt(query.cursor ?? '0', 10);
882 let terminalCursor = query.terminalCursor;
883 let lastLogCursor = query.logCursor ?? null;
884 if (Number.isNaN(lastSequence) || lastSequence < 0) {
885 lastSequence = 0;
886 }
887
888 let active = true;
889 let lastStatusSignature: string | null = null;
890 let intervalId: NodeJS.Timeout | undefined;
891 // eslint-disable-next-line prefer-const -- initialized later after cleanup function is defined
892 let heartbeatId: NodeJS.Timeout | undefined;
893 let earliestEventTimestamp: number | null = null;
894 let latestEventTimestamp: number | null = null;
895
896 const send = (event: string, payload: unknown) => {
897 if (!active) {
898 return;
899 }
900 try {
901 res.write(`event: ${event}\n`);
902 res.write(`data: ${JSON.stringify(payload)}\n\n`);
903 // Flush headers if available (helps with immediate delivery)
904 if (typeof (res as any).flush === 'function') {
905 (res as any).flush();
906 }
907 } catch (error) {
908 // Connection closed or error writing
909 console.warn(`Failed to send SSE event ${event}:`, error);
910 if (!active) {
911 return;
912 }
913 active = false;
914 void cleanup();
915 }
916 };
917
918 const cleanup = async () => {
919 if (!active) {
920 return;

Callers 2

executionStore.tsFile · 0.80
connectFunction · 0.80

Calls 7

pumpFunction · 0.85
ensureRunAccessMethod · 0.80
subscribeToRunMethod · 0.80
parseMethod · 0.80
listSinceMethod · 0.80
buildDataFlowsMethod · 0.80
warnMethod · 0.80

Tested by

no test coverage detected