feat: add manual onboarding flow alias

This commit is contained in:
Peter Steinberger
2026-01-22 23:07:40 +00:00
parent 370896e994
commit 814e9a500e
6 changed files with 59 additions and 47 deletions

View File

@@ -16,6 +16,10 @@ Related:
```bash
clawdbot onboard
clawdbot onboard --flow quickstart
clawdbot onboard --flow manual
clawdbot onboard --mode remote --remote-url ws://gateway-host:18789
```
Flow notes:
- `quickstart`: minimal prompts, auto-generates a gateway token.
- `manual`: full prompts for port/bind/auth (alias of `advanced`).

View File

@@ -48,7 +48,7 @@ export function registerOnboardCommand(program: Command) {
"Acknowledge that agents are powerful and full system access is risky (required for --non-interactive)",
false,
)
.option("--flow <flow>", "Wizard flow: quickstart|advanced")
.option("--flow <flow>", "Wizard flow: quickstart|advanced|manual")
.option("--mode <mode>", "Wizard mode: local|remote")
.option(
"--auth-choice <choice>",
@@ -106,7 +106,7 @@ export function registerOnboardCommand(program: Command) {
workspace: opts.workspace as string | undefined,
nonInteractive: Boolean(opts.nonInteractive),
acceptRisk: Boolean(opts.acceptRisk),
flow: opts.flow as "quickstart" | "advanced" | undefined,
flow: opts.flow as "quickstart" | "advanced" | "manual" | undefined,
mode: opts.mode as "local" | "remote" | undefined,
authChoice: opts.authChoice as AuthChoice | undefined,
tokenProvider: opts.tokenProvider as string | undefined,

View File

@@ -42,7 +42,8 @@ export type ProviderChoice = ChannelChoice;
export type OnboardOptions = {
mode?: OnboardMode;
flow?: "quickstart" | "advanced";
/** "manual" is an alias for "advanced". */
flow?: "quickstart" | "advanced" | "manual";
workspace?: string;
nonInteractive?: boolean;
/** Required for non-interactive onboarding; skips the interactive risk prompt when true. */

View File

@@ -12,7 +12,9 @@ import type { OnboardOptions } from "./onboard-types.js";
export async function onboardCommand(opts: OnboardOptions, runtime: RuntimeEnv = defaultRuntime) {
assertSupportedRuntime(runtime);
const authChoice = opts.authChoice === "oauth" ? ("setup-token" as const) : opts.authChoice;
const normalizedOpts = authChoice === opts.authChoice ? opts : { ...opts, authChoice };
const flow = opts.flow === "manual" ? ("advanced" as const) : opts.flow;
const normalizedOpts =
authChoice === opts.authChoice && flow === opts.flow ? opts : { ...opts, authChoice, flow };
if (normalizedOpts.nonInteractive && normalizedOpts.acceptRisk !== true) {
runtime.error(

View File

@@ -191,7 +191,7 @@ export async function configureGatewayForOnboarding(
const tokenInput = await prompter.text({
message: "Gateway token (blank to generate)",
placeholder: "Needed for multi-machine or non-loopback access",
initialValue: quickstartGateway.token ?? randomToken(),
initialValue: quickstartGateway.token ?? "",
});
gatewayToken = String(tokenInput).trim() || randomToken();
}

View File

@@ -82,10 +82,9 @@ export async function runOnboardingWizard(
const snapshot = await readConfigFileSnapshot();
let baseConfig: ClawdbotConfig = snapshot.valid ? snapshot.config : {};
if (snapshot.exists) {
const title = snapshot.valid ? "Existing config detected" : "Invalid config";
await prompter.note(summarizeExistingConfig(baseConfig), title);
if (!snapshot.valid && snapshot.issues.length > 0) {
if (snapshot.exists && !snapshot.valid) {
await prompter.note(summarizeExistingConfig(baseConfig), "Invalid config");
if (snapshot.issues.length > 0) {
await prompter.note(
[
...snapshot.issues.map((iss) => `- ${iss.path}: ${iss.message}`),
@@ -95,14 +94,51 @@ export async function runOnboardingWizard(
"Config issues",
);
}
await prompter.outro(
`Config invalid. Run \`${formatCliCommand("clawdbot doctor")}\` to repair it, then re-run onboarding.`,
);
runtime.exit(1);
return;
}
if (!snapshot.valid) {
await prompter.outro(
`Config invalid. Run \`${formatCliCommand("clawdbot doctor")}\` to repair it, then re-run onboarding.`,
);
runtime.exit(1);
return;
}
const quickstartHint = `Configure details later via ${formatCliCommand("clawdbot configure")}.`;
const manualHint = "Configure port, network, Tailscale, and auth options.";
const explicitFlowRaw = opts.flow?.trim();
const normalizedExplicitFlow = explicitFlowRaw === "manual" ? "advanced" : explicitFlowRaw;
if (
normalizedExplicitFlow &&
normalizedExplicitFlow !== "quickstart" &&
normalizedExplicitFlow !== "advanced"
) {
runtime.error("Invalid --flow (use quickstart, manual, or advanced).");
runtime.exit(1);
return;
}
const explicitFlow: WizardFlow | undefined =
normalizedExplicitFlow === "quickstart" || normalizedExplicitFlow === "advanced"
? normalizedExplicitFlow
: undefined;
let flow: WizardFlow =
explicitFlow ??
((await prompter.select({
message: "Onboarding mode",
options: [
{ value: "quickstart", label: "QuickStart", hint: quickstartHint },
{ value: "advanced", label: "Manual", hint: manualHint },
],
initialValue: "quickstart",
})) as "quickstart" | "advanced");
if (opts.mode === "remote" && flow === "quickstart") {
await prompter.note(
"QuickStart only supports local gateways. Switching to Manual mode.",
"QuickStart",
);
flow = "advanced";
}
if (snapshot.exists) {
await prompter.note(summarizeExistingConfig(baseConfig), "Existing config detected");
const action = (await prompter.select({
message: "Config handling",
@@ -134,37 +170,6 @@ export async function runOnboardingWizard(
}
}
const quickstartHint = `Configure details later via ${formatCliCommand("clawdbot configure")}.`;
const advancedHint = "Configure port, network, Tailscale, and auth options.";
const explicitFlowRaw = opts.flow?.trim();
if (explicitFlowRaw && explicitFlowRaw !== "quickstart" && explicitFlowRaw !== "advanced") {
runtime.error("Invalid --flow (use quickstart or advanced).");
runtime.exit(1);
return;
}
const explicitFlow: WizardFlow | undefined =
explicitFlowRaw === "quickstart" || explicitFlowRaw === "advanced"
? explicitFlowRaw
: undefined;
let flow: WizardFlow =
explicitFlow ??
((await prompter.select({
message: "Onboarding mode",
options: [
{ value: "quickstart", label: "QuickStart", hint: quickstartHint },
{ value: "advanced", label: "Advanced", hint: advancedHint },
],
initialValue: "quickstart",
})) as "quickstart" | "advanced");
if (opts.mode === "remote" && flow === "quickstart") {
await prompter.note(
"QuickStart only supports local gateways. Switching to Advanced mode.",
"QuickStart",
);
flow = "advanced";
}
const quickstartGateway: QuickstartGatewayDefaults = (() => {
const hasExisting =
typeof baseConfig.gateway?.port === "number" ||