diff --git a/CHANGELOG.md b/CHANGELOG.md index 233f70a3f33..eff3f91cc34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Memory/LanceDB: expose public memory artifacts through the active memory provider bridge so memory-wiki imports durable memory files, daily notes, dream reports, and event logs without depending on memory-core internals. Fixes #83604. (#85060) Thanks @brokemac79. +- Docker setup: stop printing the Gateway bearer token in setup logs and printed follow-up commands. - Agents: let embedded compaction fallback retries proceed when PI-compatible candidates do not need agent harness plugin preparation. - Agents/tools: honor configured custom provider API keys when deciding whether media, image-generation, video-generation, music-generation, and PDF tools are available. (#85570) - StepFun: stop advertising stale generic API key auth choices so onboarding only offers runtime-backed Standard and Step Plan choices. diff --git a/scripts/docker/setup.sh b/scripts/docker/setup.sh index af454e7a1bd..ee24538375f 100755 --- a/scripts/docker/setup.sh +++ b/scripts/docker/setup.sh @@ -572,11 +572,17 @@ else else echo "Bonjour/mDNS advertising: explicitly enabled (OPENCLAW_DISABLE_BONJOUR=$OPENCLAW_DISABLE_BONJOUR)." fi - echo "Gateway token: $OPENCLAW_GATEWAY_TOKEN" + echo "Gateway token: stored in Docker environment/config (not printed)." echo "Tailscale exposure: Off (use host-level tailnet/Tailscale setup separately)." echo "Install Gateway daemon: No (managed by Docker Compose)" echo "" - run_prestart_cli onboard --mode local --no-install-daemon + run_prestart_cli onboard \ + --mode local \ + --no-install-daemon \ + --gateway-auth token \ + --gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN \ + --skip-ui \ + --suppress-gateway-token-output fi echo "" @@ -711,8 +717,8 @@ echo "Gateway running with host port mapping." echo "Access from tailnet devices via the host's tailnet IP." echo "Config: $OPENCLAW_CONFIG_DIR" echo "Workspace: $OPENCLAW_WORKSPACE_DIR" -echo "Token: $OPENCLAW_GATEWAY_TOKEN" +echo "Token: stored in Docker environment/config (not printed)." echo "" echo "Commands:" echo " ${COMPOSE_HINT} logs -f openclaw-gateway" -echo " ${COMPOSE_HINT} exec openclaw-gateway node dist/index.js health --token \"$OPENCLAW_GATEWAY_TOKEN\"" +echo " ${COMPOSE_HINT} exec openclaw-gateway sh -lc 'node dist/index.js health --token \"\$OPENCLAW_GATEWAY_TOKEN\"'" diff --git a/src/cli/program/register.onboard.ts b/src/cli/program/register.onboard.ts index c85a9d5c685..346b8fdec14 100644 --- a/src/cli/program/register.onboard.ts +++ b/src/cli/program/register.onboard.ts @@ -167,6 +167,7 @@ export function registerOnboardCommand(program: Command): void { .option("--skip-search", "Skip search provider setup") .option("--skip-health", "Skip health check") .option("--skip-ui", "Skip Control UI/TUI prompts") + .option("--suppress-gateway-token-output", "Suppress token-bearing Gateway/UI output") .option("--skip-hooks", "Skip hook setup") .option("--node-manager ", "Node manager for skills: npm|pnpm|bun") .option("--import-from ", "Migration provider to run during onboarding") @@ -246,6 +247,7 @@ export function registerOnboardCommand(program: Command): void { skipSearch: Boolean(opts.skipSearch), skipHealth: Boolean(opts.skipHealth), skipUi: Boolean(opts.skipUi), + suppressGatewayTokenOutput: Boolean(opts.suppressGatewayTokenOutput), skipHooks: Boolean(opts.skipHooks), nodeManager: opts.nodeManager as NodeManagerChoice | undefined, importFrom: opts.importFrom as string | undefined, diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts index bd523699636..de3e8f9da86 100644 --- a/src/commands/onboard-types.ts +++ b/src/commands/onboard-types.ts @@ -79,6 +79,7 @@ export type OnboardOptions = OnboardDynamicProviderOptions & { skipSearch?: boolean; skipHealth?: boolean; skipUi?: boolean; + suppressGatewayTokenOutput?: boolean; skipHooks?: boolean; nodeManager?: NodeManagerChoice; remoteUrl?: string; diff --git a/src/docker-setup.e2e.test.ts b/src/docker-setup.e2e.test.ts index ba8b79ef0d2..807fd78631b 100644 --- a/src/docker-setup.e2e.test.ts +++ b/src/docker-setup.e2e.test.ts @@ -141,7 +141,8 @@ function runDockerSetup( cwd: sandbox.rootDir, env: createEnv(sandbox, overrides), encoding: "utf8", - stdio: ["ignore", "ignore", "pipe"], + maxBuffer: 4 * 1024 * 1024, + stdio: ["ignore", "pipe", "pipe"], }); } @@ -284,8 +285,11 @@ describe("scripts/docker/setup.sh", () => { const log = await readDockerLog(activeSandbox); expect(log).toContain("--build-arg OPENCLAW_IMAGE_APT_PACKAGES=curl wget"); expect(log).toContain( - `run --rm --no-deps ${prestartContainerEnvFlags} --entrypoint node openclaw-gateway dist/index.js onboard --mode local --no-install-daemon`, + `run --rm --no-deps ${prestartContainerEnvFlags} --entrypoint node openclaw-gateway dist/index.js onboard --mode local --no-install-daemon --gateway-auth token --gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN --skip-ui --suppress-gateway-token-output`, ); + expect(result.stdout).toContain("Gateway token: stored in Docker environment/config"); + expect(result.stdout).not.toContain("test-token"); + expect(result.stdout).not.toContain("#token="); expect(log).toContain( `run --rm --no-deps ${prestartContainerEnvFlags} --entrypoint node openclaw-gateway dist/index.js config set --batch-json [{"path":"gateway.mode","value":"local"},{"path":"gateway.bind","value":"lan"},{"path":"gateway.controlUi.allowedOrigins","value":["http://localhost:18789","http://127.0.0.1:18789"]}]`, ); @@ -702,7 +706,9 @@ describe("scripts/docker/setup.sh", () => { expect(result.status).toBe(0); const log = await readDockerLog(activeSandbox); - expect(log).toContain("onboard --mode local --no-install-daemon"); + expect(log).toContain( + "onboard --mode local --no-install-daemon --gateway-auth token --gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN --skip-ui --suppress-gateway-token-output", + ); const envFile = await readFile(join(activeSandbox.rootDir, ".env"), "utf8"); expect(envFile).toMatch(/OPENCLAW_SKIP_ONBOARDING=\n/); }); diff --git a/src/wizard/setup.finalize.test.ts b/src/wizard/setup.finalize.test.ts index fc5af08dcad..431401722bf 100644 --- a/src/wizard/setup.finalize.test.ts +++ b/src/wizard/setup.finalize.test.ts @@ -594,6 +594,43 @@ describe("finalizeSetupWizard", () => { expect(gatewayServiceInstall).toHaveBeenCalledTimes(1); }); + it("suppresses token-bearing onboarding output when requested", async () => { + const prompter = createLaterPrompter(); + + await finalizeSetupWizard({ + flow: "advanced", + opts: { + acceptRisk: true, + authChoice: "skip", + installDaemon: false, + skipHealth: true, + skipUi: true, + suppressGatewayTokenOutput: true, + }, + baseConfig: {}, + nextConfig: {}, + workspaceDir: "/tmp", + settings: { + port: 18789, + bind: "loopback", + authMode: "token", + gatewayToken: "session-token", + tailscaleMode: "off", + tailscaleResetOnExit: false, + }, + prompter, + runtime: createRuntime(), + }); + + const output = vi + .mocked(prompter.note) + .mock.calls.map((call) => call.join("\n")) + .join("\n"); + expect(output).toContain("http://127.0.0.1:18789"); + expect(output).not.toContain("session-token"); + expect(output).not.toContain("#token="); + }); + it("stops after a scheduled restart instead of reinstalling the service", async () => { const progressUpdate = vi.fn(); const progressStop = vi.fn(); diff --git a/src/wizard/setup.finalize.ts b/src/wizard/setup.finalize.ts index b0bae801266..7975e307e27 100644 --- a/src/wizard/setup.finalize.ts +++ b/src/wizard/setup.finalize.ts @@ -77,6 +77,7 @@ export async function finalizeSetupWizard( options: FinalizeOnboardingOptions, ): Promise<{ launchedTui: boolean }> { const { flow, opts, baseConfig, nextConfig, settings, prompter, runtime } = options; + const suppressGatewayTokenOutput = opts.suppressGatewayTokenOutput === true; let gatewayProbe: { ok: boolean; detail?: string } = { ok: true }; let resolvedGatewayPassword = ""; @@ -392,7 +393,7 @@ export async function finalizeSetupWizard( tlsEnabled: nextConfig.gateway?.tls?.enabled === true, }); const authedUrl = - settings.authMode === "token" && settings.gatewayToken + settings.authMode === "token" && settings.gatewayToken && !suppressGatewayTokenOutput ? `${links.httpUrl}#token=${encodeURIComponent(settings.gatewayToken)}` : links.httpUrl; if (opts.skipHealth || !gatewayProbe.ok) { @@ -419,7 +420,7 @@ export async function finalizeSetupWizard( await prompter.note( [ t("wizard.finalize.webUiUrl", { url: links.httpUrl }), - settings.authMode === "token" && settings.gatewayToken + settings.authMode === "token" && settings.gatewayToken && !suppressGatewayTokenOutput ? t("wizard.finalize.webUiWithTokenUrl", { url: authedUrl }) : undefined, t("wizard.finalize.gatewayWsUrl", { url: links.wsUrl }), @@ -450,24 +451,22 @@ export async function finalizeSetupWizard( } if (gatewayProbe.ok) { - await prompter.note( - [ - t("wizard.finalize.gatewayTokenShared"), - t("wizard.finalize.gatewayTokenStored"), - t("wizard.finalize.gatewayTokenView", { - command: formatCliCommand("openclaw config get gateway.auth.token"), - }), - t("wizard.finalize.gatewayTokenGenerate", { - command: formatCliCommand("openclaw doctor --generate-gateway-token"), - }), - t("wizard.finalize.dashboardTokenMemory"), - t("wizard.finalize.dashboardOpenAnytime", { - command: formatCliCommand("openclaw dashboard --no-open"), - }), - t("wizard.finalize.dashboardTokenPrompt"), - ].join("\n"), - "Token", - ); + const tokenNotes = [ + t("wizard.finalize.gatewayTokenShared"), + t("wizard.finalize.gatewayTokenStored"), + t("wizard.finalize.gatewayTokenView", { + command: formatCliCommand("openclaw config get gateway.auth.token"), + }), + t("wizard.finalize.gatewayTokenGenerate", { + command: formatCliCommand("openclaw doctor --generate-gateway-token"), + }), + suppressGatewayTokenOutput ? undefined : t("wizard.finalize.dashboardTokenMemory"), + t("wizard.finalize.dashboardOpenAnytime", { + command: formatCliCommand("openclaw dashboard --no-open"), + }), + suppressGatewayTokenOutput ? undefined : t("wizard.finalize.dashboardTokenPrompt"), + ].filter(Boolean); + await prompter.note(tokenNotes.join("\n"), "Token"); } const hatchOptions: { value: "tui" | "web" | "later"; label: string }[] = [ @@ -505,14 +504,20 @@ export async function finalizeSetupWizard( controlUiOpenHint = formatControlUiSshHint({ port: settings.port, basePath: controlUiBasePath, - token: settings.authMode === "token" ? settings.gatewayToken : undefined, + token: + settings.authMode === "token" && !suppressGatewayTokenOutput + ? settings.gatewayToken + : undefined, }); } } else { controlUiOpenHint = formatControlUiSshHint({ port: settings.port, basePath: controlUiBasePath, - token: settings.authMode === "token" ? settings.gatewayToken : undefined, + token: + settings.authMode === "token" && !suppressGatewayTokenOutput + ? settings.gatewayToken + : undefined, }); } await prompter.note( @@ -553,6 +558,7 @@ export async function finalizeSetupWizard( gatewayProbe.ok && settings.authMode === "token" && Boolean(settings.gatewayToken) && + !suppressGatewayTokenOutput && hatchChoice === null; if (shouldOpenControlUi) { const browserSupport = await detectBrowserOpenSupport();