feat(onboard): show setup timeline note (#97482) · openclaw/openclaw@a537947
ly85206559
·
2026-06-29
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -328,7 +328,12 @@ and troubleshooting see the main [FAQ](/help/faq).
|
328 | 328 | Rough guide: |
329 | 329 | |
330 | 330 | - **Install:** 2-5 minutes |
331 | | -- **Onboarding:** 5-15 minutes depending on how many channels/models you configure |
| 331 | +- **QuickStart onboarding:** usually a few minutes |
| 332 | +- **Full onboarding:** longer when provider sign-in, channel pairing, daemon install, |
| 333 | + network downloads, skills, or optional plugins need extra setup |
| 334 | + |
| 335 | +The CLI wizard shows this timeline up front. You can skip optional steps and return |
| 336 | +later with `openclaw configure`. |
332 | 337 | |
333 | 338 | If it hangs, use [Installer stuck](#quick-start-and-first-run-setup) |
334 | 339 | and the fast debug loop in [I am stuck](#quick-start-and-first-run-setup). |
@@ -508,7 +513,8 @@ and troubleshooting see the main [FAQ](/help/faq).
|
508 | 513 | - **Daemon install** (LaunchAgent on macOS; systemd user unit on Linux/WSL2) |
509 | 514 | - **Health checks** and **skills** selection |
510 | 515 | |
511 | | -It also warns if your configured model is unknown or missing auth. |
| 516 | +It also sets duration expectations before the main prompts begin and warns if your |
| 517 | +configured model is unknown or missing auth. |
512 | 518 | |
513 | 519 | </Accordion> |
514 | 520 | |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -55,7 +55,10 @@ Need to install Node? See [Node setup](/install/node).
|
55 | 55 | ``` |
56 | 56 | |
57 | 57 | The wizard walks you through choosing a model provider, setting an API key, |
58 | | -and configuring the Gateway. It takes about 2 minutes. |
| 58 | +and configuring the Gateway. QuickStart is usually only a few minutes, but |
| 59 | +provider sign-in, channel pairing, daemon install, network downloads, skills, |
| 60 | +or optional plugins can make full onboarding take longer. You can skip optional |
| 61 | +steps and return later with `openclaw configure`. |
59 | 62 | |
60 | 63 | See [Onboarding (CLI)](/start/wizard) for the full reference. |
61 | 64 | |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -17,6 +17,12 @@ and workspace defaults in one guided flow.
|
17 | 17 | openclaw onboard |
18 | 18 | ``` |
19 | 19 | |
| 20 | +QuickStart is usually only a few minutes, but full onboarding can take longer |
| 21 | +when provider sign-in, channel pairing, daemon install, network downloads, |
| 22 | +skills, or optional plugins need extra setup. The wizard shows this timeline up |
| 23 | +front, and optional steps can be skipped and revisited later with |
| 24 | +`openclaw configure`. |
| 25 | + |
20 | 26 | ## Locale |
21 | 27 | |
22 | 28 | The CLI wizard localizes fixed onboarding copy. It resolves locale from |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -244,15 +244,14 @@ describe("FS tools with workspaceOnly=false", () => {
|
244 | 244 | await fs.writeFile(allowedAbsolutePath, "seed"); |
245 | 245 | |
246 | 246 | const tools = [ |
247 | | -createOpenClawReadTool(createReadTool(workspaceDir) as unknown as AnyAgentTool), |
248 | 247 | wrapToolMemoryFlushAppendOnlyWrite(createHostWorkspaceWriteTool(workspaceDir), { |
249 | 248 | root: workspaceDir, |
250 | 249 | relativePath: allowedRelativePath, |
251 | 250 | }), |
252 | 251 | ]; |
253 | 252 | |
254 | 253 | const writeTool = requireTool(tools, "write"); |
255 | | -expect(tools.map((tool) => tool.name).toSorted()).toEqual(["read", "write"]); |
| 254 | +expect(tools.map((tool) => tool.name)).toEqual(["write"]); |
256 | 255 | |
257 | 256 | await expect( |
258 | 257 | writeTool.execute("test-call-memory-deny", { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -62,6 +62,18 @@ async function createTempSessionFile(): Promise<string> {
|
62 | 62 | return sessionFile; |
63 | 63 | } |
64 | 64 | |
| 65 | +async function waitUntil(predicate: () => boolean, message: string): Promise<void> { |
| 66 | +const deadline = Date.now() + 1_000; |
| 67 | +while (!predicate()) { |
| 68 | +if (Date.now() >= deadline) { |
| 69 | +throw new Error(message); |
| 70 | +} |
| 71 | +await new Promise<void>((resolve) => { |
| 72 | +setTimeout(resolve, 1); |
| 73 | +}); |
| 74 | +} |
| 75 | +} |
| 76 | + |
65 | 77 | function cloneBigIntStatWith( |
66 | 78 | stat: Awaited<ReturnType<typeof fs.stat>>, |
67 | 79 | fields: Partial<Awaited<ReturnType<typeof fs.stat>>>, |
@@ -3976,15 +3988,16 @@ describe("embedded attempt session lock lifecycle", () => {
|
3976 | 3988 | .finally(() => { |
3977 | 3989 | takeoverSettled = true; |
3978 | 3990 | }); |
3979 | | -await new Promise<void>((resolve) => { |
3980 | | -setImmediate(resolve); |
3981 | | -}); |
| 3991 | +await waitUntil( |
| 3992 | +() => controller.hasSessionTakeover(), |
| 3993 | +"expected takeover detection while active retained writer was still running", |
| 3994 | +); |
3982 | 3995 | |
3983 | 3996 | expect(takeoverSettled).toBe(false); |
3984 | 3997 | expect(releaseRetained).not.toHaveBeenCalled(); |
3985 | 3998 | |
3986 | 3999 | releaseActiveWrite(); |
3987 | | -await expect(activeWrite).resolves.toBeUndefined(); |
| 4000 | +await expect(activeWrite).rejects.toBeInstanceOf(EmbeddedAttemptSessionTakeoverError); |
3988 | 4001 | await expect(takeoverWrite).resolves.toBeInstanceOf(EmbeddedAttemptSessionTakeoverError); |
3989 | 4002 | |
3990 | 4003 | expect(releaseRetained).toHaveBeenCalledTimes(1); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -238,6 +238,9 @@ export const en = {
|
238 | 238 | authChoiceRequired: "auth choice is required", |
239 | 239 | channelsTitle: "Channels", |
240 | 240 | configHandling: "Config handling", |
| 241 | +durationNote: |
| 242 | +"We'll walk through model/auth, workspace, Gateway, channels, web search, skills, and optional plugin setup. QuickStart is usually only a few minutes, but provider sign-in, channel pairing, daemon install, network downloads, and optional dependencies can take longer. You can skip optional steps and return later with {command}.", |
| 243 | +durationTitle: "Setup timeline", |
241 | 244 | existingConfigTitle: "Existing config detected", |
242 | 245 | flowAdvanced: "Manual setup", |
243 | 246 | flowAdvancedHint: "Choose Gateway port, network exposure, Tailscale, and auth.", |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -235,6 +235,9 @@ export const zh_CN = {
|
235 | 235 | authChoiceRequired: "必须选择认证方式", |
236 | 236 | channelsTitle: "频道", |
237 | 237 | configHandling: "配置处理", |
| 238 | +durationNote: |
| 239 | +"接下来会依次设置模型/认证、工作区、Gateway、频道、网页搜索、技能和可选插件。QuickStart 通常只需几分钟,但提供商登录、频道配对、daemon 安装、网络下载和可选依赖可能需要更久。你可以跳过可选步骤,之后用 {command} 返回配置。", |
| 240 | +durationTitle: "设置流程", |
238 | 241 | existingConfigTitle: "检测到已有配置", |
239 | 242 | flowAdvanced: "手动设置", |
240 | 243 | flowAdvancedHint: "选择 Gateway 端口、网络暴露、Tailscale 和认证方式。", |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -235,6 +235,9 @@ export const zh_TW = {
|
235 | 235 | authChoiceRequired: "必須選擇認證方式", |
236 | 236 | channelsTitle: "頻道", |
237 | 237 | configHandling: "設定處理", |
| 238 | +durationNote: |
| 239 | +"接下來會依序設定模型/認證、工作區、Gateway、頻道、網頁搜尋、技能和選用插件。QuickStart 通常只需幾分鐘,但提供者登入、頻道配對、daemon 安裝、網路下載和選用依賴可能需要更久。你可以略過選用步驟,之後用 {command} 返回設定。", |
| 240 | +durationTitle: "設定流程", |
238 | 241 | existingConfigTitle: "偵測到既有設定", |
239 | 242 | flowAdvanced: "手動設定", |
240 | 243 | flowAdvancedHint: "選擇 Gateway 連接埠、網路暴露、Tailscale 和認證方式。", |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -509,6 +509,35 @@ describe("runSetupWizard", () => {
|
509 | 509 | expect(healthCommand).not.toHaveBeenCalled(); |
510 | 510 | expect(runTui).not.toHaveBeenCalled(); |
511 | 511 | }); |
| 512 | + |
| 513 | +it("shows setup duration expectations before the security acknowledgement", async () => { |
| 514 | +const note: WizardPrompter["note"] = vi.fn(async () => {}); |
| 515 | +const confirm = vi.fn(async () => true) as unknown as WizardPrompter["confirm"]; |
| 516 | +const prompter = buildWizardPrompter({ note, confirm }); |
| 517 | +const runtime = createRuntime({ throwsOnExit: true }); |
| 518 | + |
| 519 | +await runSetupWizard( |
| 520 | +{ |
| 521 | +flow: "quickstart", |
| 522 | +authChoice: "skip", |
| 523 | +installDaemon: false, |
| 524 | +skipProviders: true, |
| 525 | +skipSkills: true, |
| 526 | +skipSearch: true, |
| 527 | +skipHealth: true, |
| 528 | +skipUi: true, |
| 529 | +}, |
| 530 | +runtime, |
| 531 | +prompter, |
| 532 | +); |
| 533 | + |
| 534 | +const calls = getWizardNoteCalls(note); |
| 535 | +expect(calls[0]?.[1]).toBe("Setup timeline"); |
| 536 | +expect(calls[0]?.[0]).toContain("model/auth, workspace, Gateway, channels"); |
| 537 | +expect(calls[0]?.[0]).toContain("openclaw configure"); |
| 538 | +expect(calls[1]?.[1]).toBe("Security disclaimer"); |
| 539 | +}); |
| 540 | + |
512 | 541 | it("persists skipBootstrap and skips workspace bootstrap creation when requested", async () => { |
513 | 542 | ensureWorkspaceAndSessions.mockClear(); |
514 | 543 | replaceConfigFile.mockClear(); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -231,6 +231,12 @@ export async function runSetupWizard(
|
231 | 231 | const onboardHelpers = await import("../commands/onboard-helpers.js"); |
232 | 232 | onboardHelpers.printWizardHeader(runtime); |
233 | 233 | await prompter.intro(t("wizard.setup.intro")); |
| 234 | +await prompter.note( |
| 235 | +t("wizard.setup.durationNote", { |
| 236 | +command: formatCliCommand("openclaw configure"), |
| 237 | +}), |
| 238 | +t("wizard.setup.durationTitle"), |
| 239 | +); |
234 | 240 | await requireRiskAcknowledgement({ opts, prompter }); |
235 | 241 | |
236 | 242 | const snapshot = await readSetupConfigFileSnapshot(); |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。