From bed24721219a6a99066e09bad2b92739c0651f87 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 21 Apr 2026 03:45:21 +0100 Subject: [PATCH] fix: stabilize docker live checks --- scripts/docker/install-sh-nonroot/Dockerfile | 1 + scripts/docker/install-sh-smoke/run.sh | 2 +- .../gateway-models.profiles.live.test.ts | 13 +++++++++ src/terminal/note.ts | 28 +++++++++++++++++-- src/terminal/table.test.ts | 10 ++++++- 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/scripts/docker/install-sh-nonroot/Dockerfile b/scripts/docker/install-sh-nonroot/Dockerfile index 0c41ef8a938..ab3f7e9a97f 100644 --- a/scripts/docker/install-sh-nonroot/Dockerfile +++ b/scripts/docker/install-sh-nonroot/Dockerfile @@ -39,6 +39,7 @@ WORKDIR /home/app ENV NPM_CONFIG_FUND=false ENV NPM_CONFIG_AUDIT=false +ENV NPM_CONFIG_UPDATE_NOTIFIER=false COPY install-sh-common/cli-verify.sh /usr/local/install-sh-common/cli-verify.sh COPY install-sh-common/version-parse.sh /usr/local/install-sh-common/version-parse.sh diff --git a/scripts/docker/install-sh-smoke/run.sh b/scripts/docker/install-sh-smoke/run.sh index a48236deb4d..3205a1b4713 100755 --- a/scripts/docker/install-sh-smoke/run.sh +++ b/scripts/docker/install-sh-smoke/run.sh @@ -14,7 +14,7 @@ UPDATE_BASELINE_TAG_URL="${OPENCLAW_INSTALL_UPDATE_BASELINE_TAG_URL:-}" UPDATE_EXPECT_VERSION="${OPENCLAW_INSTALL_UPDATE_EXPECT_VERSION:-}" UPDATE_TAG_URL="${OPENCLAW_INSTALL_UPDATE_TAG_URL:-}" HEARTBEAT_INTERVAL="${OPENCLAW_INSTALL_SMOKE_HEARTBEAT_INTERVAL:-60}" -INSTALL_COMMAND_TIMEOUT="${OPENCLAW_INSTALL_SMOKE_COMMAND_TIMEOUT:-300}" +INSTALL_COMMAND_TIMEOUT="${OPENCLAW_INSTALL_SMOKE_COMMAND_TIMEOUT:-900}" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # shellcheck source=../install-sh-common/cli-verify.sh diff --git a/src/gateway/gateway-models.profiles.live.test.ts b/src/gateway/gateway-models.profiles.live.test.ts index 3fa22cf1ffa..dad70ddb8e8 100644 --- a/src/gateway/gateway-models.profiles.live.test.ts +++ b/src/gateway/gateway-models.profiles.live.test.ts @@ -1373,6 +1373,19 @@ async function runGatewayModelSuite(params: GatewayModelSuiteParams) { const workspaceDir = resolveAgentWorkspaceDir(params.cfg, agentId); await fs.mkdir(workspaceDir, { recursive: true }); + await fs.mkdir(path.join(workspaceDir, ".openclaw"), { recursive: true }); + await fs.writeFile( + path.join(workspaceDir, ".openclaw", "workspace-state.json"), + `${JSON.stringify( + { + version: 1, + setupCompletedAt: new Date().toISOString(), + }, + null, + 2, + )}\n`, + ); + await fs.rm(path.join(workspaceDir, "BOOTSTRAP.md"), { force: true }); const nonceA = randomUUID(); const nonceB = randomUUID(); const toolProbePath = path.join(workspaceDir, `.openclaw-live-tool-probe.${nonceA}.txt`); diff --git a/src/terminal/note.ts b/src/terminal/note.ts index 06fc7f48186..0978f32770c 100644 --- a/src/terminal/note.ts +++ b/src/terminal/note.ts @@ -3,6 +3,7 @@ import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; import { visibleWidth } from "./ansi.js"; import { stylePromptTitle } from "./prompt-style.js"; +const MIN_NOTE_COLUMNS = 80; const URL_PREFIX_RE = /^(https?:\/\/|file:\/\/)/i; const WINDOWS_DRIVE_RE = /^[a-zA-Z]:[\\/]/; const FILE_LIKE_RE = /^[a-zA-Z0-9._-]+$/; @@ -150,7 +151,7 @@ export function wrapNoteMessage( message: string, options: { maxWidth?: number; columns?: number } = {}, ): string { - const columns = options.columns ?? process.stdout.columns ?? 80; + const columns = options.columns ?? resolveNoteColumns(process.stdout.columns); const maxWidth = options.maxWidth ?? Math.max(40, Math.min(88, columns - 10)); return message .split("\n") @@ -158,9 +159,32 @@ export function wrapNoteMessage( .join("\n"); } +export function resolveNoteColumns(columns: number | undefined): number { + if (!Number.isFinite(columns) || !columns || columns < MIN_NOTE_COLUMNS) { + return MIN_NOTE_COLUMNS; + } + return columns; +} + +function createNoteOutput(columns: number): NodeJS.WriteStream { + if (process.stdout.columns === columns) { + return process.stdout; + } + const output = Object.create(process.stdout) as NodeJS.WriteStream; + Object.defineProperty(output, "columns", { + value: columns, + configurable: true, + }); + output.write = process.stdout.write.bind(process.stdout); + return output; +} + export function note(message: string, title?: string) { if (isSuppressedByEnv(process.env.OPENCLAW_SUPPRESS_NOTES)) { return; } - clackNote(wrapNoteMessage(message), stylePromptTitle(title)); + const columns = resolveNoteColumns(process.stdout.columns); + clackNote(wrapNoteMessage(message, { columns }), stylePromptTitle(title), { + output: createNoteOutput(columns), + }); } diff --git a/src/terminal/table.test.ts b/src/terminal/table.test.ts index bad2fe48cf2..dae19b0faaf 100644 --- a/src/terminal/table.test.ts +++ b/src/terminal/table.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { visibleWidth } from "./ansi.js"; -import { wrapNoteMessage } from "./note.js"; +import { resolveNoteColumns, wrapNoteMessage } from "./note.js"; import { renderTable } from "./table.js"; describe("renderTable", () => { @@ -270,4 +270,12 @@ describe("wrapNoteMessage", () => { const wrapped = wrapNoteMessage(input, { maxWidth: 12, columns: 80 }); expect(wrapped).toBe(input); }); + + it("clamps bogus TTY columns before clack wraps note text", () => { + expect(resolveNoteColumns(undefined)).toBe(80); + expect(resolveNoteColumns(0)).toBe(80); + expect(resolveNoteColumns(1)).toBe(80); + expect(resolveNoteColumns(79)).toBe(80); + expect(resolveNoteColumns(120)).toBe(120); + }); });