Skip to content

Commit 80955e7

Browse files
[cli] [web] Allow opening web UI without config validation, show better UI when runs can't be found (#684)
1 parent f989613 commit 80955e7

File tree

20 files changed

+294
-135
lines changed

20 files changed

+294
-135
lines changed

.changeset/vast-files-judge.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@workflow/cli": patch
3+
---
4+
5+
Allow opening UI without a valid local config detected, UI will show warning and watch folder

packages/cli/src/commands/cancel.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ export default class Cancel extends BaseCommand {
3232
const { flags, args } = await this.parse(Cancel);
3333

3434
const world = await setupCliWorld(flags, this.config.version);
35+
if (!world) {
36+
throw new Error(
37+
'Failed to connect to backend. Check your configuration.'
38+
);
39+
}
3540

3641
await cancelRun(world, args.runId);
3742
}

packages/cli/src/commands/inspect.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,24 @@ export default class Inspect extends BaseCommand {
141141

142142
const id = args.id;
143143

144-
const world = await setupCliWorld(flags, this.config.version);
144+
// For web mode, allow config errors so we can open the web UI for configuration
145+
const isWebMode = flags.web || resource === 'web';
146+
const world = await setupCliWorld(flags, this.config.version, isWebMode);
145147

146148
// Handle web UI mode
147-
if (flags.web || resource === 'web') {
149+
if (isWebMode) {
148150
const actualResource = resource === 'web' ? 'run' : resource;
149151
await launchWebUI(actualResource, id, flags, this.config.version);
150152
process.exit(0);
151153
}
152154

155+
// For non-web commands, we need a valid world
156+
if (!world) {
157+
throw new Error(
158+
'Failed to connect to backend. Check your configuration.'
159+
);
160+
}
161+
153162
// Convert flags to InspectCLIOptions with proper typing
154163
const options = toInspectOptions(flags);
155164

packages/cli/src/commands/start.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ export default class Start extends BaseCommand {
4747
const stringArgs = restArgs.map((arg) => String(arg));
4848

4949
const world = await setupCliWorld(flags, this.config.version);
50+
if (!world) {
51+
throw new Error(
52+
'Failed to connect to backend. Check your configuration.'
53+
);
54+
}
5055

5156
await startRun(world, args.workflowName, flags, stringArgs);
5257
}

packages/cli/src/commands/web.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ export default class Web extends BaseCommand {
4949

5050
// Setup the CLI world to write env vars from flags
5151
// This ensures backend, authToken, team, project, etc. are properly set
52-
await setupCliWorld(flags, this.config.version);
52+
// Don't throw on config errors - let the web UI handle them
53+
await setupCliWorld(flags, this.config.version, true);
5354

5455
// Launch web UI with 'run' as the default resource
5556
await launchWebUI('run', id, flags, this.config.version);

packages/cli/src/lib/inspect/env.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { access } from 'node:fs/promises';
22
import { join, resolve } from 'node:path';
3-
import {
4-
findWorkflowDataDir,
5-
possibleWorkflowDataPaths,
6-
} from '@workflow/utils/check-data-dir';
3+
import { findWorkflowDataDir } from '@workflow/utils/check-data-dir';
74
import { logger } from '../config/log.js';
85
import { getWorkflowConfig } from '../config/workflow-config.js';
96
import { getAuth } from './auth.js';
@@ -76,6 +73,8 @@ async function findManifestPath(cwd: string) {
7673
/**
7774
* Overwrites process.env variables related to local world configuration,
7875
* if relevant environment variables aren't set already.
76+
*
77+
* Throws if the workflow data directory can not be found.
7978
*/
8079
export const inferLocalWorldEnvVars = async () => {
8180
const envVars = getEnvVars();
@@ -93,7 +92,7 @@ export const inferLocalWorldEnvVars = async () => {
9392
// Infer workflow data directory
9493
if (!envVars.WORKFLOW_LOCAL_DATA_DIR) {
9594
const localResult = await findWorkflowDataDir(cwd);
96-
if (localResult) {
95+
if (localResult.dataDir) {
9796
logger.debug('Found workflow data directory:', localResult.dataDir);
9897
envVars.WORKFLOW_LOCAL_DATA_DIR = localResult.dataDir;
9998
writeEnvVars(envVars);
@@ -102,21 +101,15 @@ export const inferLocalWorldEnvVars = async () => {
102101
repoRoot = await findRepoRoot(cwd, cwd);
103102
if (repoRoot) {
104103
const repoResult = await findWorkflowDataDir(repoRoot);
105-
if (repoResult) {
104+
if (repoResult.dataDir) {
106105
logger.debug('Found workflow data directory:', repoResult.dataDir);
107106
envVars.WORKFLOW_LOCAL_DATA_DIR = repoResult.dataDir;
108107
writeEnvVars(envVars);
109108
}
110109
}
111-
112110
if (!envVars.WORKFLOW_LOCAL_DATA_DIR) {
113-
logger.error(
114-
`No workflow data directory found in "${cwd}". Have you run any workflows yet?`
115-
);
116-
logger.warn(
117-
`\nCheck whether your data is in any of:\n${possibleWorkflowDataPaths.map((p: string) => ` ${cwd}/${p}${repoRoot && repoRoot !== cwd ? `\n ${repoRoot}/${p}` : ''}`).join('\n')}\n`
118-
);
119-
throw new Error('No workflow data directory found');
111+
const message = `No workflow data directory found in "${cwd}". Have you run any workflows yet?`;
112+
throw new Error(message);
120113
}
121114
}
122115
}

packages/cli/src/lib/inspect/output.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,11 @@ const showTable = (
347347
TABLE_TRUNCATE_IO_LENGTH = displaySettings.dataFieldWidth;
348348

349349
// Show status legend if using abbreviated status
350-
if (displaySettings.abbreviateStatus && visibleProps.includes('status')) {
350+
if (
351+
data.length > 0 &&
352+
displaySettings.abbreviateStatus &&
353+
visibleProps.includes('status')
354+
) {
351355
showStatusLegend();
352356
}
353357

packages/cli/src/lib/inspect/setup.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ import {
88
writeEnvVars,
99
} from './env.js';
1010

11+
/**
12+
* Setup CLI world configuration.
13+
* If throwOnConfigError is false, will return null world with the error message
14+
* instead of throwing, allowing the web UI to open for configuration.
15+
*/
1116
export const setupCliWorld = async (
1217
flags: {
1318
json: boolean;
@@ -18,8 +23,9 @@ export const setupCliWorld = async (
1823
project: string;
1924
team: string;
2025
},
21-
version: string
22-
) => {
26+
version: string,
27+
ignoreLocalWorldConfigError = false
28+
): Promise<Awaited<ReturnType<typeof createWorld>> | null> => {
2329
setJsonMode(Boolean(flags.json));
2430
setVerboseMode(Boolean(flags.verbose));
2531

@@ -30,9 +36,9 @@ export const setupCliWorld = async (
3036

3137
logger.showBox(
3238
'green',
33-
` Workflow CLI v${version} `,
34-
` Docs at ${docsUrl} `,
35-
chalk.yellow('This is a beta release - commands might change')
39+
`Workflow CLI v${version}`,
40+
`Docs at ${docsUrl}`,
41+
chalk.yellow('This is a beta release')
3642
);
3743

3844
logger.debug('Inferring env vars, backend:', flags.backend);
@@ -55,9 +61,25 @@ export const setupCliWorld = async (
5561
flags.backend === 'local' ||
5662
flags.backend === '@workflow/world-local'
5763
) {
58-
await inferLocalWorldEnvVars();
64+
try {
65+
await inferLocalWorldEnvVars();
66+
} catch (error) {
67+
if (ignoreLocalWorldConfigError) {
68+
const configError =
69+
error instanceof Error
70+
? error.message
71+
: 'Unknown configuration error';
72+
logger.warn(
73+
'Failed to find valid local world configuration:',
74+
configError
75+
);
76+
return null;
77+
}
78+
throw error;
79+
}
5980
}
6081

6182
logger.debug('Initializing world');
62-
return createWorld();
83+
const world = await createWorld();
84+
return world;
6385
};

packages/cli/src/lib/inspect/web.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,9 @@ export async function launchWebUI(
304304
const alreadyRunning = await isServerRunning(hostUrl);
305305

306306
if (alreadyRunning) {
307-
logger.info(chalk.cyan('Web UI server is already running'));
308-
logger.info(chalk.cyan(`Access at: ${hostUrl}`));
307+
logger.info(
308+
chalk.cyan(`Web UI server is already running on port ${webPort}.`)
309+
);
309310
} else {
310311
// Start the server
311312
const started = await startWebServer(webPort);

0 commit comments

Comments
 (0)