Skip to content

Commit a3c754f

Browse files
committed
Clarify web build cooldown errors
1 parent fd25998 commit a3c754f

3 files changed

Lines changed: 58 additions & 39 deletions

File tree

package-lock.json

Lines changed: 25 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/projects/cli.test.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ describe("runProjectSubcommand", () => {
216216
expect(cancelled).toEqual(["wrong-session"]);
217217
});
218218

219-
it("explains payment challenges from the web build endpoint", async () => {
219+
it("points plan-limit responses to usage", async () => {
220220
const client = {
221221
startBuild: async () => {
222222
throw new ProjectClientError("request failed: 402", 402);
@@ -227,8 +227,25 @@ describe("runProjectSubcommand", () => {
227227
const result = await runProject(["project", "build", "Build", "it"], client);
228228

229229
expect(result.code).toBe(1);
230-
expect(result.stderr.join("\n")).toContain("payment challenge");
231-
expect(result.stderr.join("\n")).toContain("web build OAuth gate");
230+
expect(result.stderr.join("\n")).toContain("codebase usage");
231+
});
232+
233+
it("formats build cooldown retry guidance", async () => {
234+
const client = {
235+
startBuild: async () => {
236+
throw new ProjectClientError(
237+
"request failed: 429 build_cooldown — Build cooldown active. Retry in 313s.",
238+
429,
239+
313_000,
240+
);
241+
},
242+
hasCredentials: () => true,
243+
} as unknown as ProjectClient;
244+
245+
const result = await runProject(["project", "build", "Build", "it"], client);
246+
247+
expect(result.code).toBe(1);
248+
expect(result.stderr.join("\n")).toContain("retry in 5m 13s");
232249
});
233250

234251
it("waits for a completed build and prints its preview URL", async () => {

src/projects/cli.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,14 @@ export async function runProjectSubcommand(argv: string[], options: ProjectCliOp
7373
if (e instanceof ProjectClientError) {
7474
err(`error: ${e.message}`);
7575
if (e.status === 402) {
76-
err(
77-
"hint: codebase.design returned a payment challenge before accepting OAuth. Run `codebase auth login`; if this persists, the web build OAuth gate needs to be deployed.",
78-
);
76+
err("hint: run `codebase usage` to check your remaining builds, plan limits, and reset date.");
7977
}
8078
if (e.status === 403) {
8179
err("hint: run `codebase auth login` again so the CLI can request builds:read/builds:write.");
8280
}
81+
if (e.status === 429 && e.retryAfterMs) {
82+
err(`hint: retry in ${formatRetryDelay(e.retryAfterMs)}.`);
83+
}
8384
return e.status === 404 ? 4 : 1;
8485
}
8586
err(`error: ${e instanceof Error ? e.message : String(e)}`);
@@ -526,6 +527,15 @@ function formatTimelineDuration(durationMs: number): string {
526527
return `${(durationMs / 1000).toFixed(durationMs < 10_000 ? 1 : 0)}s`;
527528
}
528529

530+
function formatRetryDelay(durationMs: number): string {
531+
const totalSeconds = Math.max(1, Math.ceil(durationMs / 1000));
532+
const minutes = Math.floor(totalSeconds / 60);
533+
const seconds = totalSeconds % 60;
534+
if (minutes === 0) return `${seconds}s`;
535+
if (seconds === 0) return `${minutes}m`;
536+
return `${minutes}m ${seconds}s`;
537+
}
538+
529539
async function printPreview(
530540
client: ProjectClient,
531541
sessionId: string,

0 commit comments

Comments
 (0)