diff --git a/CHANGELOG.md b/CHANGELOG.md index d18915ed2d9..64766a362f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Agents/models: forward model `maxTokens` as the default output-token limit for OpenAI-compatible Responses and Completions transports when no runtime override is provided, preventing provider defaults from silently truncating larger outputs. (#76645) Thanks @joeyfrasier. +- macOS CLI/onboarding: honor sensitive wizard text steps in `openclaw-mac wizard` with termios no-echo input, suppressing saved credential previews while preserving long API keys and gateway tokens. Fixes #76698. Thanks @anurag-bg-neu and @sallyom. - Control UI/Skills: fix skill detail modal silently failing to open in all browsers by deferring `showModal()` until the dialog element is connected to the DOM; the Lit `ref` callback fired before connection causing a `DOMException: HTMLDialogElement.showModal: Dialog element is not connected` on every skill click. Thanks @nickmopen. - Gateway/update: run `doctor --non-interactive --fix` after Control UI global package updates before reporting success, so legacy config is migrated before the gateway restart. Thanks @stevenchouai. - Gateway/cron: stop a lazy cron startup that loses a hot-reload race, preventing the old cron service from starting after reload has already replaced cron state. diff --git a/apps/macos/Sources/OpenClawMacCLI/WizardCommand.swift b/apps/macos/Sources/OpenClawMacCLI/WizardCommand.swift index 26ccdb0e0a6..dbf1828b4aa 100644 --- a/apps/macos/Sources/OpenClawMacCLI/WizardCommand.swift +++ b/apps/macos/Sources/OpenClawMacCLI/WizardCommand.swift @@ -445,6 +445,12 @@ private func promptAnswer(for step: WizardStep) throws -> Any { case "text": let initial = anyCodableString(step.initialvalue) let prompt = step.placeholder ?? "Value" + if step.sensitive == true { + let sensitivePrompt = initial.isEmpty ? prompt : "\(prompt) (leave blank to keep existing)" + let value = try readSensitiveLineWithPrompt(sensitivePrompt) + let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines) + return trimmed.isEmpty ? initial : trimmed + } let value = try readLineWithPrompt("\(prompt)\(initial.isEmpty ? "" : " [\(initial)]")") let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines) return trimmed.isEmpty ? initial : trimmed @@ -524,3 +530,28 @@ private func readLineWithPrompt(_ prompt: String) throws -> String { } return line } + +private func readSensitiveLineWithPrompt(_ prompt: String) throws -> String { + print("\(prompt): ", terminator: "") + fflush(stdout) + + var original = termios() + guard tcgetattr(STDIN_FILENO, &original) == 0 else { + throw WizardCliError.gatewayError("Could not configure hidden terminal input.") + } + + var hidden = original + hidden.c_lflag &= ~tcflag_t(ECHO) + guard tcsetattr(STDIN_FILENO, TCSANOW, &hidden) == 0 else { + throw WizardCliError.gatewayError("Could not configure hidden terminal input.") + } + defer { + _ = tcsetattr(STDIN_FILENO, TCSANOW, &original) + print("") + } + + guard let line = readLine() else { + throw WizardCliError.cancelled + } + return line +}