From 1c8371c320d6b20bc912fdb0be325663f3f94c5f Mon Sep 17 00:00:00 2001 From: chengzeyi Date: Wed, 17 Jun 2026 01:14:36 +0800 Subject: [PATCH 1/2] Handle sync timeout in CLI --- README.md | 1 + package-lock.json | 4 +-- src/commands/run.ts | 59 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index def40bc..493e095 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ wavespeed config [--default-model …] View / update CLI defaults # generation wavespeed run [model|alias] -p "…" Run any model or alias (uses defaultModel if omitted) +wavespeed run --sync Attempt sync wait; timed-out tasks remain queryable wavespeed run -h Dynamic schema-based help (alias-aware) wavespeed schema Pretty-print a model's input schema wavespeed models [query] Browse the live catalog (cached 1h) diff --git a/package-lock.json b/package-lock.json index 47d1eb7..1a7b99e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@wavespeed/cli", - "version": "0.1.0", + "version": "0.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@wavespeed/cli", - "version": "0.1.0", + "version": "0.2.2", "license": "MIT", "dependencies": { "@inquirer/prompts": "^7.0.0", diff --git a/src/commands/run.ts b/src/commands/run.ts index 28edcd9..12a8559 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -19,6 +19,29 @@ function extractUrls(output: any): string[] { return []; } +type SyncTimeoutDetails = { + predictionId?: string; + resultUrl?: string; +}; + +function cleanTrailingPunctuation(value: string): string { + return value.replace(/[).,]+$/, ''); +} + +function extractSyncTimeoutDetails(message: string): SyncTimeoutDetails | null { + if (!message.includes('Sync mode timed out')) return null; + + const taskIdMatch = + message.match(/task_id:\s*([^)]+)/) ?? + message.match(/Prediction ID:\s*([^\s.]+)/); + const resultUrlMatch = message.match(/Query the result later at:\s*(\S+)/); + + return { + predictionId: taskIdMatch?.[1] ? cleanTrailingPunctuation(taskIdMatch[1]) : undefined, + resultUrl: resultUrlMatch?.[1] ? cleanTrailingPunctuation(resultUrlMatch[1]) : undefined, + }; +} + function failMissingModel(): never { const msg = 'No model specified. Pass a model ID as the first argument, ' + @@ -51,7 +74,7 @@ export function registerRun(program: Command): void { .option('-p, --prompt ', 'Shorthand for --input prompt=') .option('--input-file ', 'JSON file with full input object') .option('--download [path]', 'Save outputs locally (optional path template, e.g. "./out/{index}.{ext}")') - .option('--sync', 'Use synchronous mode (single request, no polling)') + .option('--sync', 'Attempt sync mode; timed-out tasks can still be queried later') .option('--json', 'Emit a single JSON object on stdout (progress goes to stderr)') .option('--output-dir ', 'Directory for --download when no path is given') .action(async (modelArg: string | undefined, opts: any) => { @@ -101,8 +124,38 @@ export function registerRun(program: Command): void { } spinner.succeed(`Done in ${Math.floor((Date.now() - startedAt) / 1000)}s.`); } catch (err: any) { - spinner.fail(err.message ?? String(err)); - if (isJsonMode()) emitJsonError(err.message ?? String(err)); + const message = err.message ?? String(err); + const syncTimeout = opts.sync ? extractSyncTimeoutDetails(message) : null; + + if (syncTimeout && !isJsonMode()) { + spinner.fail('Sync mode timed out; the task is still processing asynchronously.'); + log(chalk.gray(' reason: ') + message); + if (syncTimeout.predictionId) { + log(chalk.gray(' prediction: ') + chalk.white(syncTimeout.predictionId)); + log(chalk.gray(' query: ') + chalk.cyan(`wavespeed show ${syncTimeout.predictionId}`)); + } + if (syncTimeout.resultUrl) { + log(chalk.gray(' result URL: ') + chalk.cyan(syncTimeout.resultUrl)); + } + } else { + spinner.fail(message); + } + + if (isJsonMode()) { + emitJsonError( + message, + syncTimeout + ? { + sync_timeout: true, + prediction_id: syncTimeout.predictionId, + result_url: syncTimeout.resultUrl, + query_command: syncTimeout.predictionId + ? `wavespeed show ${syncTimeout.predictionId}` + : undefined, + } + : {}, + ); + } process.exit(1); } From 92ce56ac520ee699c0905e9f086e3e3f45642151 Mon Sep 17 00:00:00 2001 From: chengzeyi Date: Wed, 17 Jun 2026 01:33:10 +0800 Subject: [PATCH 2/2] Bump version to 0.2.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a7b99e..b0d8606 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@wavespeed/cli", - "version": "0.2.2", + "version": "0.2.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@wavespeed/cli", - "version": "0.2.2", + "version": "0.2.3", "license": "MIT", "dependencies": { "@inquirer/prompts": "^7.0.0", diff --git a/package.json b/package.json index 9a00f31..f64bc90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@wavespeed/cli", - "version": "0.2.2", + "version": "0.2.3", "description": "WaveSpeed CLI — one command to run any WaveSpeed AI model from your terminal.", "publishConfig": { "access": "public"