Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/fix-project-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@workflow/cli": patch
"@workflow/core": patch
"@workflow/web": patch
"@workflow/world-vercel": patch
---

Separate project ID and project name into distinct env vars (WORKFLOW_VERCEL_PROJECT and WORKFLOW_VERCEL_PROJECT_NAME)
21 changes: 18 additions & 3 deletions packages/cli/src/lib/inspect/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const getEnvVars = (): Record<string, string> => {
WORKFLOW_VERCEL_ENV: env.WORKFLOW_VERCEL_ENV || '',
WORKFLOW_VERCEL_AUTH_TOKEN: env.WORKFLOW_VERCEL_AUTH_TOKEN || '',
WORKFLOW_VERCEL_PROJECT: env.WORKFLOW_VERCEL_PROJECT || '',
WORKFLOW_VERCEL_PROJECT_NAME: env.WORKFLOW_VERCEL_PROJECT_NAME || '',
WORKFLOW_VERCEL_TEAM: env.WORKFLOW_VERCEL_TEAM || '',
WORKFLOW_LOCAL_UI: env.WORKFLOW_LOCAL_UI || '',
PORT: env.PORT || '',
Expand Down Expand Up @@ -184,13 +185,27 @@ export const inferVercelProjectAndTeam = async () => {
export const inferVercelEnvVars = async () => {
const envVars = getEnvVars();

if (!envVars.WORKFLOW_VERCEL_PROJECT || !envVars.WORKFLOW_VERCEL_TEAM) {
// Infer project/team from .vercel folder when:
// - WORKFLOW_VERCEL_PROJECT or WORKFLOW_VERCEL_TEAM is missing, OR
// - WORKFLOW_VERCEL_PROJECT is set but doesn't look like a real project ID
// (e.g., user passed a slug via --project flag), OR
// - WORKFLOW_VERCEL_PROJECT_NAME is missing (need to populate the slug)
const needsInference =
!envVars.WORKFLOW_VERCEL_PROJECT ||
!envVars.WORKFLOW_VERCEL_TEAM ||
!envVars.WORKFLOW_VERCEL_PROJECT_NAME ||
!envVars.WORKFLOW_VERCEL_PROJECT.startsWith('prj_');

if (needsInference) {
logger.debug('Inferring vercel project and team from .vercel folder');
const inferredProject = await inferVercelProjectAndTeam();
if (inferredProject) {
const { projectId, projectName, teamId } = inferredProject;
envVars.WORKFLOW_VERCEL_PROJECT = projectName || projectId;
envVars.WORKFLOW_VERCEL_TEAM = teamId;
// WORKFLOW_VERCEL_PROJECT is the real project ID (e.g., prj_xxx)
envVars.WORKFLOW_VERCEL_PROJECT = projectId;
// WORKFLOW_VERCEL_PROJECT_NAME is the project slug (e.g., my-app)
envVars.WORKFLOW_VERCEL_PROJECT_NAME = projectName || projectId;
Comment on lines +204 to +207
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inferVercelEnvVars() only populates WORKFLOW_VERCEL_PROJECT_NAME (and normalizes WORKFLOW_VERCEL_PROJECT to a real prj_… id) when either WORKFLOW_VERCEL_PROJECT or WORKFLOW_VERCEL_TEAM is missing. If the CLI flag/env already sets WORKFLOW_VERCEL_PROJECT to a project slug/name (which the --project flag description currently implies), this block won’t run, and the world will continue sending the slug in the x-vercel-project-id header—reintroducing the exact bug this PR is fixing and potentially breaking auth/encryption context. Consider expanding the guard to also run when WORKFLOW_VERCEL_PROJECT_NAME is missing and/or when WORKFLOW_VERCEL_PROJECT doesn’t look like a Vercel project id (e.g. not prj_…), so the env vars are consistently normalized.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inference logic normalizes the env var to a real project ID when the linked project is available. If the user sets WORKFLOW_VERCEL_PROJECT to a slug, the inference step replaces it with the real ID from .vercel/project.json.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — the guard now also runs when WORKFLOW_VERCEL_PROJECT does not start with prj_ or when WORKFLOW_VERCEL_PROJECT_NAME is missing, ensuring consistent normalization regardless of how the env vars were initially populated.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guard on line 188 (!envVars.WORKFLOW_VERCEL_PROJECT || !envVars.WORKFLOW_VERCEL_TEAM) means inference is skipped entirely when both are already set. But WORKFLOW_VERCEL_PROJECT could contain a slug from the --project CLI flag (see flags.ts:58 and setup.ts:78), and WORKFLOW_VERCEL_PROJECT_NAME would remain empty.

Consider also running normalization when WORKFLOW_VERCEL_PROJECT is set but doesn't look like a real project ID (e.g., doesn't start with prj_), or when WORKFLOW_VERCEL_PROJECT_NAME is missing. This would make the env vars robust regardless of how they were initially populated.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Expanded the guard to also run inference when:

  • WORKFLOW_VERCEL_PROJECT does not start with prj_ (e.g., slug from --project flag)
  • WORKFLOW_VERCEL_PROJECT_NAME is missing

Also changed the team assignment to preserve an explicitly set team (envVars.WORKFLOW_VERCEL_TEAM || teamId) so only missing values get populated during normalization.

envVars.WORKFLOW_VERCEL_TEAM = envVars.WORKFLOW_VERCEL_TEAM || teamId;
writeEnvVars(envVars);
} else {
logger.warn(
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/lib/inspect/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ export async function launchWebUI(
envVars.WORKFLOW_TARGET_WORLD
);
const teamSlug = envVars.WORKFLOW_VERCEL_TEAM;
const projectName = envVars.WORKFLOW_VERCEL_PROJECT;
const projectName =
envVars.WORKFLOW_VERCEL_PROJECT_NAME || envVars.WORKFLOW_VERCEL_PROJECT;

// Check if user wants local UI via flag or environment variable
const useLocalUi = flags.localUi;
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/runtime/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export const createWorld = (): World => {
token: process.env.WORKFLOW_VERCEL_AUTH_TOKEN,
projectConfig: {
environment: process.env.WORKFLOW_VERCEL_ENV,
projectId: process.env.WORKFLOW_VERCEL_PROJECT,
projectId: process.env.WORKFLOW_VERCEL_PROJECT, // real ID (prj_xxx)
projectName: process.env.WORKFLOW_VERCEL_PROJECT_NAME, // slug (my-app)
teamId: process.env.WORKFLOW_VERCEL_TEAM,
},
});
Expand Down
5 changes: 5 additions & 0 deletions packages/web/app/server/workflow-server-actions.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,15 @@ const WORLD_ENV_ALLOWLIST_BY_TARGET_WORLD: Record<string, string[]> = {
'WORKFLOW_VERCEL_ENV',
'WORKFLOW_VERCEL_TEAM',
'WORKFLOW_VERCEL_PROJECT',
'WORKFLOW_VERCEL_PROJECT_NAME',
'WORKFLOW_VERCEL_AUTH_TOKEN',
],
'@workflow/world-vercel': [
'WORKFLOW_TARGET_WORLD',
'WORKFLOW_VERCEL_ENV',
'WORKFLOW_VERCEL_TEAM',
'WORKFLOW_VERCEL_PROJECT',
'WORKFLOW_VERCEL_PROJECT_NAME',
'WORKFLOW_VERCEL_AUTH_TOKEN',
],

Expand Down Expand Up @@ -432,6 +434,9 @@ async function getWorldFromEnv(userEnvMap: EnvMap): Promise<World> {
projectId:
userEnvMap.WORKFLOW_VERCEL_PROJECT ||
process.env.WORKFLOW_VERCEL_PROJECT,
projectName:
userEnvMap.WORKFLOW_VERCEL_PROJECT_NAME ||
process.env.WORKFLOW_VERCEL_PROJECT_NAME,
teamId:
userEnvMap.WORKFLOW_VERCEL_TEAM || process.env.WORKFLOW_VERCEL_TEAM,
},
Expand Down
3 changes: 3 additions & 0 deletions packages/world-vercel/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ export interface APIConfig {
token?: string;
headers?: RequestInit['headers'];
projectConfig?: {
/** The real Vercel project ID (e.g., prj_xxx) */
projectId?: string;
/** The project name/slug (e.g., my-app), used for dashboard URLs */
projectName?: string;
teamId?: string;
environment?: string;
};
Expand Down