diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 032015e37..48236e88d 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -1194,6 +1194,12 @@ Control UI base path: - Examples: `"/ui"`, `"/clawdbot"`, `"/apps/clawdbot"`. - Default: root (`/`) (unchanged). +Related docs: +- [Control UI](/web/control-ui) +- [Web overview](/web) +- [Tailscale](/gateway/tailscale) +- [Remote access](/gateway/remote) + Notes: - `clawdbot gateway` refuses to start unless `gateway.mode` is set to `local` (or you pass the override flag). - `gateway.port` controls the single multiplexed port used for WebSocket + HTTP (control UI, hooks, A2UI). diff --git a/src/commands/configure.ts b/src/commands/configure.ts index 027f35dd0..60a56516f 100644 --- a/src/commands/configure.ts +++ b/src/commands/configure.ts @@ -150,6 +150,14 @@ async function promptGatewayConfig( let tailscaleResetOnExit = false; if (tailscaleMode !== "off") { + note( + [ + "Docs:", + "https://docs.clawd.bot/gateway/tailscale", + "https://docs.clawd.bot/web", + ].join("\n"), + "Tailscale", + ); tailscaleResetOnExit = Boolean( guardCancel( await confirm({ @@ -300,6 +308,7 @@ async function promptAuthConfig( } catch (err) { spin.stop("OAuth failed"); runtime.error(String(err)); + note("Trouble with OAuth? See https://docs.clawd.bot/start/faq", "OAuth"); } } else if (authChoice === "openai-codex") { const isRemote = isRemoteEnvironment(); @@ -373,6 +382,7 @@ async function promptAuthConfig( } catch (err) { spin.stop("OpenAI OAuth failed"); runtime.error(String(err)); + note("Trouble with OAuth? See https://docs.clawd.bot/start/faq", "OAuth"); } } else if (authChoice === "antigravity") { const isRemote = isRemoteEnvironment(); @@ -447,6 +457,7 @@ async function promptAuthConfig( } catch (err) { spin.stop("Antigravity OAuth failed"); runtime.error(String(err)); + note("Trouble with OAuth? See https://docs.clawd.bot/start/faq", "OAuth"); } } else if (authChoice === "apiKey") { const key = guardCancel( @@ -608,9 +619,11 @@ export async function runConfigureWizard( note(summarizeExistingConfig(baseConfig), title); if (!snapshot.valid && snapshot.issues.length > 0) { note( - snapshot.issues - .map((iss) => `- ${iss.path}: ${iss.message}`) - .join("\n"), + [ + ...snapshot.issues.map((iss) => `- ${iss.path}: ${iss.message}`), + "", + "Docs: https://docs.clawd.bot/gateway/configuration", + ].join("\n"), "Config issues", ); } @@ -789,6 +802,14 @@ export async function runConfigureWizard( await healthCommand({ json: false, timeoutMs: 10_000 }, runtime); } catch (err) { runtime.error(`Health check failed: ${String(err)}`); + note( + [ + "Docs:", + "https://docs.clawd.bot/gateway/health", + "https://docs.clawd.bot/gateway/troubleshooting", + ].join("\n"), + "Health check help", + ); } } @@ -797,20 +818,22 @@ export async function runConfigureWizard( runtime.error(controlUiAssets.message); } - note( - (() => { - const bind = nextConfig.gateway?.bind ?? "loopback"; - const links = resolveControlUiLinks({ - bind, - port: gatewayPort, - basePath: nextConfig.gateway?.controlUi?.basePath, - }); - return [`Web UI: ${links.httpUrl}`, `Gateway WS: ${links.wsUrl}`].join( - "\n", - ); - })(), - "Control UI", - ); + note( + (() => { + const bind = nextConfig.gateway?.bind ?? "loopback"; + const links = resolveControlUiLinks({ + bind, + port: gatewayPort, + basePath: nextConfig.gateway?.controlUi?.basePath, + }); + return [ + `Web UI: ${links.httpUrl}`, + `Gateway WS: ${links.wsUrl}`, + "Docs: https://docs.clawd.bot/web/control-ui", + ].join("\n"); + })(), + "Control UI", + ); const browserSupport = await detectBrowserOpenSupport(); if (!browserSupport.ok) { diff --git a/src/commands/onboard-helpers.ts b/src/commands/onboard-helpers.ts index 43e91e33d..3cd1a48d7 100644 --- a/src/commands/onboard-helpers.ts +++ b/src/commands/onboard-helpers.ts @@ -195,6 +195,9 @@ export function formatControlUiSshHint(params: { "Then open:", localUrl, authedUrl, + "Docs:", + "https://docs.clawd.bot/gateway/remote", + "https://docs.clawd.bot/web/control-ui", ] .filter(Boolean) .join("\n"); diff --git a/src/commands/onboard-providers.ts b/src/commands/onboard-providers.ts index 4ac6f032f..d061affd6 100644 --- a/src/commands/onboard-providers.ts +++ b/src/commands/onboard-providers.ts @@ -39,6 +39,7 @@ async function noteProviderPrimer(prompter: WizardPrompter): Promise { "DM security: default is pairing; unknown DMs get a pairing code.", "Approve with: clawdbot pairing approve --provider ", 'Public DMs require dmPolicy="open" + allowFrom=["*"].', + "Docs: https://docs.clawd.bot/start/pairing", "", "WhatsApp: links via WhatsApp Web (scan QR), stores creds for future sends.", "WhatsApp: dedicated second number recommended; primary number OK (self-chat).", @@ -59,6 +60,7 @@ async function noteTelegramTokenHelp(prompter: WizardPrompter): Promise { "2) Run /newbot (or /mybots)", "3) Copy the token (looks like 123456:ABC...)", "Tip: you can also set TELEGRAM_BOT_TOKEN in your env.", + "Docs: https://docs.clawd.bot/telegram", ].join("\n"), "Telegram bot token", ); @@ -71,6 +73,7 @@ async function noteDiscordTokenHelp(prompter: WizardPrompter): Promise { "2) Bot → Add Bot → Reset Token → copy token", "3) OAuth2 → URL Generator → scope 'bot' → invite to your server", "Tip: enable Message Content Intent if you need message text.", + "Docs: https://docs.clawd.bot/discord", ].join("\n"), "Discord bot token", ); @@ -158,6 +161,7 @@ async function noteSlackTokenHelp( "4) Enable Event Subscriptions (socket) for message events", "5) App Home → enable the Messages tab for DMs", "Tip: set SLACK_BOT_TOKEN + SLACK_APP_TOKEN in your env.", + "Docs: https://docs.clawd.bot/slack", "", "Manifest (JSON):", manifest, @@ -298,6 +302,7 @@ async function maybeConfigureDmPolicies(params: { "Default: pairing (unknown DMs get a pairing code).", `Approve: clawdbot pairing approve --provider ${params.provider} `, `Public DMs: ${params.policyKey}="open" + ${params.allowFromKey} includes "*".`, + "Docs: https://docs.clawd.bot/start/pairing", ].join("\n"), `${params.label} DM access`, ); @@ -383,6 +388,7 @@ async function promptWhatsAppAllowFrom( "- disabled: ignore WhatsApp DMs", "", `Current: dmPolicy=${existingPolicy}, allowFrom=${existingLabel}`, + "Docs: https://docs.clawd.bot/whatsapp", ].join("\n"), "WhatsApp DM access", ); @@ -551,6 +557,7 @@ export async function setupProviders( [ "Scan the QR with WhatsApp on your phone.", `Credentials are stored under ${WA_WEB_AUTH_DIR}/ for future runs.`, + "Docs: https://docs.clawd.bot/whatsapp", ].join("\n"), "WhatsApp linking", ); @@ -566,6 +573,10 @@ export async function setupProviders( await loginWeb(false, "web"); } catch (err) { runtime.error(`WhatsApp login failed: ${String(err)}`); + await prompter.note( + "Docs: https://docs.clawd.bot/whatsapp", + "WhatsApp help", + ); } } else if (!whatsappLinked) { await prompter.note( @@ -864,6 +875,7 @@ export async function setupProviders( 'Link device with: signal-cli link -n "Clawdbot"', "Scan QR in Signal → Linked Devices", "Then run: clawdbot gateway call providers.status --params '{\"probe\":true}'", + "Docs: https://docs.clawd.bot/signal", ].join("\n"), "Signal next steps", ); @@ -902,6 +914,7 @@ export async function setupProviders( "Ensure Clawdbot has Full Disk Access to Messages DB.", "Grant Automation permission for Messages when prompted.", "List chats with: imsg chats --limit 20", + "Docs: https://docs.clawd.bot/imessage", ].join("\n"), "iMessage next steps", ); diff --git a/src/commands/onboard-remote.ts b/src/commands/onboard-remote.ts index e08b73230..61d62b54d 100644 --- a/src/commands/onboard-remote.ts +++ b/src/commands/onboard-remote.ts @@ -42,7 +42,10 @@ export async function promptRemoteGatewayConfig( if (!hasBonjourTool) { await prompter.note( - "Bonjour discovery requires dns-sd (macOS) or avahi-browse (Linux).", + [ + "Bonjour discovery requires dns-sd (macOS) or avahi-browse (Linux).", + "Docs: https://docs.clawd.bot/gateway/discovery", + ].join("\n"), "Discovery", ); } @@ -98,6 +101,7 @@ export async function promptRemoteGatewayConfig( `ssh -N -L 18789:127.0.0.1:18789 @${host}${ selectedBeacon.sshPort ? ` -p ${selectedBeacon.sshPort}` : "" }`, + "Docs: https://docs.clawd.bot/gateway/remote", ].join("\n"), "SSH tunnel", ); diff --git a/src/commands/onboard-skills.ts b/src/commands/onboard-skills.ts index 207480379..abab220cf 100644 --- a/src/commands/onboard-skills.ts +++ b/src/commands/onboard-skills.ts @@ -168,6 +168,7 @@ export async function setupSkills( runtime.log( "Tip: run `clawdbot doctor` to review skills + requirements.", ); + runtime.log("Docs: https://docs.clawd.bot/skills"); } } } diff --git a/src/wizard/onboarding.ts b/src/wizard/onboarding.ts index beb405b68..6b8842a8a 100644 --- a/src/wizard/onboarding.ts +++ b/src/wizard/onboarding.ts @@ -142,9 +142,11 @@ export async function runOnboardingWizard( await prompter.note(summarizeExistingConfig(baseConfig), title); if (!snapshot.valid && snapshot.issues.length > 0) { await prompter.note( - snapshot.issues - .map((iss) => `- ${iss.path}: ${iss.message}`) - .join("\n"), + [ + ...snapshot.issues.map((iss) => `- ${iss.path}: ${iss.message}`), + "", + "Docs: https://docs.clawd.bot/gateway/configuration", + ].join("\n"), "Config issues", ); } @@ -302,6 +304,10 @@ export async function runOnboardingWizard( } catch (err) { spin.stop("OAuth failed"); runtime.error(String(err)); + await prompter.note( + "Trouble with OAuth? See https://docs.clawd.bot/start/faq", + "OAuth help", + ); } } else if (authChoice === "openai-codex") { const isRemote = isRemoteEnvironment(); @@ -375,6 +381,10 @@ export async function runOnboardingWizard( } catch (err) { spin.stop("OpenAI OAuth failed"); runtime.error(String(err)); + await prompter.note( + "Trouble with OAuth? See https://docs.clawd.bot/start/faq", + "OAuth help", + ); } } else if (authChoice === "antigravity") { const isRemote = isRemoteEnvironment(); @@ -448,6 +458,10 @@ export async function runOnboardingWizard( } catch (err) { spin.stop("Antigravity OAuth failed"); runtime.error(String(err)); + await prompter.note( + "Trouble with OAuth? See https://docs.clawd.bot/start/faq", + "OAuth help", + ); } } else if (authChoice === "apiKey") { const key = await prompter.text({ @@ -520,6 +534,14 @@ export async function runOnboardingWizard( let tailscaleResetOnExit = false; if (tailscaleMode !== "off") { + await prompter.note( + [ + "Docs:", + "https://docs.clawd.bot/gateway/tailscale", + "https://docs.clawd.bot/web", + ].join("\n"), + "Tailscale", + ); tailscaleResetOnExit = Boolean( await prompter.confirm({ message: "Reset Tailscale serve/funnel on exit?", @@ -693,6 +715,14 @@ export async function runOnboardingWizard( await healthCommand({ json: false, timeoutMs: 10_000 }, runtime); } catch (err) { runtime.error(`Health check failed: ${String(err)}`); + await prompter.note( + [ + "Docs:", + "https://docs.clawd.bot/gateway/health", + "https://docs.clawd.bot/gateway/troubleshooting", + ].join("\n"), + "Health check help", + ); } const controlUiAssets = await ensureControlUiAssetsBuilt(runtime); @@ -726,6 +756,7 @@ export async function runOnboardingWizard( `Web UI: ${links.httpUrl}`, tokenParam ? `Web UI (with token): ${authedUrl}` : undefined, `Gateway WS: ${links.wsUrl}`, + "Docs: https://docs.clawd.bot/web/control-ui", ] .filter(Boolean) .join("\n"); @@ -772,5 +803,13 @@ export async function runOnboardingWizard( } } + await prompter.note( + [ + "Back up your agent workspace.", + "Docs: https://docs.clawd.bot/concepts/agent-workspace", + ].join("\n"), + "Workspace backup", + ); + await prompter.outro("Onboarding complete."); }