mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 15:59:30 +00:00
* feat(onboard): guided CLI onboarding with live AI verification and classic fallback Interactive `openclaw onboard` (and bare `openclaw` on a fresh install) now runs a guided flow with macOS-app parity: detect existing AI access, live-test candidates with a real completion before persisting anything, walk down the ladder on failure with mapped reasons, and offer verified manual API-key entry from installed provider manifests (masked input). In-flow escapes: classic wizard, Crestodian chat, skip-AI. Classic wizard gains an optional post-auth live verification step. `--classic`, `--modern`, and `--non-interactive` contracts unchanged. Docs corrected for post-#99935 routing. Closes #101851 * improve(onboard): quiet probe diagnostics in wizard TTY, carry risk ack into classic escape Candidate live-tests during guided setup are probes: rename their run id and lane to the existing probe conventions (logging/subsystem.ts console suppression, command-queue quiet probe lanes) so expected failures stop leaking raw diagnostics into the Clack UI; file diagnostics unchanged. The classic-wizard escape now passes the already-collected risk acknowledgement through instead of re-prompting in the same session. * fix(onboard): quiet the session-derived setup-inference probe lane too The live-test run enqueues on two lanes: the explicit probe lane and one derived from its temp session key. Extend the shared quiet-probe predicate to cover the derived lane so a failing candidate cannot leak lane-task diagnostics into the wizard TTY. * improve(onboard): suppress subsystem console output during wizard live tests Provider-transport subsystem loggers (model-fetch start/response, transport errors) carry no run id, so probe suppression cannot catch them and a failing candidate printed raw log lines into the Clack TTY. Reuse the TUI console subsystem-filter seam via a finally-safe scoped helper around guided activation and the classic live-verify; file logging is unchanged and the gateway (macOS app) surface is unaffected. * fix(onboard): never auto-replace a configured model when its live check fails The re-run verification probe executes outside the configured workspace (setup never runs workspace plugins), so a workspace-backed current model can fail the check while working fine in the agent. Stop the auto ladder on an existing-model failure and hand the decision to the manual stage instead of silently persisting a different candidate as the default. Docs note the fail-safe and the workspace caveat. * feat(onboard): two-way switching between Crestodian chat and the menu wizards From the chat, `open setup wizard`, `open classic wizard`, and `open channel wizard for <channel>` hand off to the guided flow, the classic wizard, or the masked `channels add` wizard after the chat TUI tears down (mirrors the open-tui handoff; gateway surface gets a text pointer instead). The hosted channel wizard no longer dead-ends at sensitive steps — it offers the switch and remembers the channel. New read-only `channel info <channel>` operation and ring-zero action surface label, blurb, configured state, and the real docs URL from channel-setup discovery so the assistant can explain Slack or Telegram prerequisites instead of guessing; both prompts instruct it to use them. `channels add --channel <id>` now preselects the channel. Docs cover the interchangeable flows. * fix(onboard): avoid param reassignment in open-setup handoff * improve(onboard): separate ask-about vs connect intent in channel prompt guidance Live test showed the agent detouring an explicit connect request through channel_info because the guidance said to consult it first. Both prompts now distinguish asking about a channel (channel info + docs link) from asking to connect (connect right away). * fix(channels): mark channel token entry as sensitive input The shared single-token prompt lacked sensitive:true, so terminal wizards echoed pasted channel tokens and the Crestodian chat bridge (which refuses plain-text secrets based on this flag) hosted the Telegram token step in visible chat. Found live-testing the chat-to-wizard switch; pre-existing on main but load-bearing for the masked-wizard contract this PR documents. * fix(onboard): restore terminal state around the guided flow's TUI launch Mirror the classic finalize handoff so the chat TUI never inherits the wizard prompter's raw/paused terminal state on the default first-run path. * fix(channels): type the token prompter mock for the sensitive-flag assertion * fix(gateway): map the TUI-only open-setup action to none for app clients Engine-side surface gating already prevents open-setup replies on the gateway surface; this keeps the client-visible action enum stable even if that gate ever regresses. (Reviewed with the switching round; missed in its commit.) * docs: regenerate docs map for onboarding page changes
120 lines
4.1 KiB
TypeScript
120 lines
4.1 KiB
TypeScript
// Crestodian CLI runner selects JSON, one-shot, or interactive setup-helper mode.
|
|
import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
|
|
import { withProgress } from "../cli/progress.js";
|
|
import { defaultRuntime, writeRuntimeJson, type RuntimeEnv } from "../runtime.js";
|
|
import type { CrestodianAssistantPlanner } from "./assistant.js";
|
|
import { resolveCrestodianOperation } from "./dialogue.js";
|
|
import {
|
|
executeCrestodianOperation,
|
|
isPersistentCrestodianOperation,
|
|
type CrestodianCommandDeps,
|
|
} from "./operations.js";
|
|
import {
|
|
formatCrestodianOverview,
|
|
loadCrestodianOverview,
|
|
type CrestodianOverview,
|
|
} from "./overview.js";
|
|
|
|
/**
|
|
* CLI entry point for Crestodian.
|
|
*
|
|
* This module chooses JSON, one-shot, or interactive TUI mode and delegates all
|
|
* command parsing/execution to dialogue and operation modules.
|
|
*/
|
|
type CrestodianInteractiveRunner = (
|
|
opts: RunCrestodianOptions,
|
|
runtime: RuntimeEnv,
|
|
) => Promise<void>;
|
|
|
|
/** Options accepted by the Crestodian command runner. */
|
|
export type RunCrestodianOptions = {
|
|
message?: string;
|
|
yes?: boolean;
|
|
json?: boolean;
|
|
interactive?: boolean;
|
|
/** "onboarding" swaps the greeting for the first-run setup proposal. */
|
|
welcomeVariant?: "onboarding";
|
|
/** Workspace override for the proposed first-run setup (from --workspace). */
|
|
setupWorkspace?: string;
|
|
/** Risk acknowledgement already collected by the calling onboarding flow. */
|
|
setupAcceptRisk?: boolean;
|
|
onReady?: () => void;
|
|
deps?: CrestodianCommandDeps;
|
|
formatOverview?: (overview: CrestodianOverview) => string;
|
|
loadOverview?: typeof loadCrestodianOverview;
|
|
planWithAssistant?: CrestodianAssistantPlanner;
|
|
input?: NodeJS.ReadableStream;
|
|
output?: NodeJS.WritableStream;
|
|
runInteractiveTui?: CrestodianInteractiveRunner;
|
|
};
|
|
|
|
function crestodianCommandDepsFromOptions(
|
|
opts: RunCrestodianOptions,
|
|
): CrestodianCommandDeps | undefined {
|
|
if (!opts.deps && !opts.formatOverview && !opts.loadOverview) {
|
|
return undefined;
|
|
}
|
|
return {
|
|
...opts.deps,
|
|
...(opts.formatOverview ? { formatOverview: opts.formatOverview } : {}),
|
|
...(opts.loadOverview ? { loadOverview: opts.loadOverview } : {}),
|
|
};
|
|
}
|
|
|
|
async function runOneShot(
|
|
input: string,
|
|
runtime: RuntimeEnv,
|
|
opts: RunCrestodianOptions,
|
|
): Promise<void> {
|
|
const operation = await resolveCrestodianOperation(input, runtime, opts);
|
|
await executeCrestodianOperation(operation, runtime, {
|
|
approved: opts.yes === true || !isPersistentCrestodianOperation(operation),
|
|
deps: crestodianCommandDepsFromOptions(opts),
|
|
});
|
|
}
|
|
|
|
/** Run Crestodian in JSON, one-shot message, or interactive TUI mode. */
|
|
export async function runCrestodian(
|
|
opts: RunCrestodianOptions = {},
|
|
runtime: RuntimeEnv = defaultRuntime,
|
|
): Promise<void> {
|
|
if (opts.json) {
|
|
const overview = await (opts.loadOverview ?? loadCrestodianOverview)();
|
|
writeRuntimeJson(runtime, overview);
|
|
return;
|
|
}
|
|
|
|
if (opts.message?.trim()) {
|
|
// One-shot mode always shows the overview first so planned changes have local context.
|
|
const overview = await withProgress(
|
|
{
|
|
label: "Loading Crestodian overview…",
|
|
indeterminate: true,
|
|
delayMs: 0,
|
|
fallback: "none",
|
|
},
|
|
async () => await (opts.loadOverview ?? loadCrestodianOverview)(),
|
|
);
|
|
runtime.log((opts.formatOverview ?? formatCrestodianOverview)(overview));
|
|
runtime.log("");
|
|
await runOneShot(opts.message, runtime, opts);
|
|
return;
|
|
}
|
|
|
|
const interactive = opts.interactive ?? true;
|
|
const input = opts.input ?? defaultStdin;
|
|
const output = opts.output ?? defaultStdout;
|
|
const inputIsTty = (input as { isTTY?: boolean }).isTTY === true;
|
|
const outputIsTty = (output as { isTTY?: boolean }).isTTY === true;
|
|
if (!interactive || !inputIsTty || !outputIsTty) {
|
|
// Without a TTY, Crestodian cannot safely ask for confirmation; require --message instead.
|
|
runtime.error("Crestodian needs an interactive TTY. Use --message for one command.");
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
|
|
const runInteractiveTui =
|
|
opts.runInteractiveTui ?? (await import("./tui-backend.js")).runCrestodianTui;
|
|
opts.onReady?.();
|
|
await runInteractiveTui(opts, runtime);
|
|
}
|