refactor: rename setup wizard surfaces

This commit is contained in:
Peter Steinberger 2026-03-15 21:39:23 -07:00
parent 07d71d2b27
commit 656848dcd7
No known key found for this signature in database
127 changed files with 617 additions and 622 deletions

View File

@ -1,9 +1,9 @@
import { setTopLevelChannelDmPolicyWithAllowFrom } from "../../../src/channels/plugins/setup-flow-helpers.js";
import {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
patchScopedAccountConfig,
} from "../../../src/channels/plugins/setup-helpers.js";
import { setTopLevelChannelDmPolicyWithAllowFrom } from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import type { DmPolicy } from "../../../src/config/types.js";

View File

@ -1,5 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js";
import type { WizardPrompter } from "../../../src/wizard/prompts.js";
import { resolveBlueBubblesAccount } from "./accounts.js";
@ -27,8 +27,8 @@ async function createBlueBubblesConfigureAdapter() {
}).config.allowFrom ?? [],
},
setup: blueBubblesSetupAdapter,
} as Parameters<typeof buildChannelSetupFlowAdapterFromSetupWizard>[0]["plugin"];
return buildChannelSetupFlowAdapterFromSetupWizard({
} as Parameters<typeof buildChannelSetupWizardAdapterFromSetupWizard>[0]["plugin"];
return buildChannelSetupWizardAdapterFromSetupWizard({
plugin,
wizard: blueBubblesSetupWizard,
});

View File

@ -1,8 +1,8 @@
import {
mergeAllowFromEntries,
resolveSetupAccountId,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import type { DmPolicy } from "../../../src/config/types.js";

View File

@ -1,3 +1,7 @@
import {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
import {
noteChannelLookupFailure,
noteChannelLookupSummary,
@ -5,12 +9,8 @@ import {
patchChannelConfigForAccount,
setLegacyChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
import {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import type { OpenClawConfig } from "../../../src/config/config.js";

View File

@ -7,8 +7,8 @@ import {
resolveSetupAccountId,
setLegacyChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js";

View File

@ -1,9 +1,9 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/feishu";
import { describe, expect, it } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { feishuPlugin } from "./channel.js";
const feishuConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const feishuConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: feishuPlugin,
wizard: feishuPlugin.setupWizard!,
});

View File

@ -1,5 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
vi.mock("./probe.js", () => ({
probeFeishu: vi.fn(async () => ({ ok: false, error: "mocked" })),
@ -56,7 +56,7 @@ async function getStatusWithEnvRefs(params: { appIdKey: string; appSecretKey: st
});
}
const feishuConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const feishuConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: feishuPlugin,
wizard: feishuPlugin.setupWizard!,
});

View File

@ -6,8 +6,8 @@ import {
setTopLevelChannelDmPolicyWithAllowFrom,
setTopLevelChannelGroupPolicy,
splitSetupEntries,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import type { DmPolicy } from "../../../src/config/types.js";

View File

@ -1,6 +1,6 @@
import type { OpenClawConfig, WizardPrompter } from "openclaw/plugin-sdk/googlechat";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { googlechatPlugin } from "./channel.js";
@ -26,7 +26,7 @@ function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
};
}
const googlechatConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const googlechatConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: googlechatPlugin,
wizard: googlechatPlugin.setupWizard!,
});

View File

@ -1,14 +1,14 @@
import {
applySetupAccountConfigPatch,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
import {
addWildcardAllowFrom,
mergeAllowFromEntries,
setTopLevelChannelDmPolicyWithAllowFrom,
splitSetupEntries,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
import {
applySetupAccountConfigPatch,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import type { DmPolicy } from "../../../src/config/types.js";

View File

@ -1,14 +1,14 @@
import {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
import {
parseSetupEntriesAllowingWildcard,
promptParsedAllowFromForScopedChannel,
setChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
import {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import { type ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import type { OpenClawConfig } from "../../../src/config/config.js";

View File

@ -3,8 +3,8 @@ import {
promptParsedAllowFromForScopedChannel,
setChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import { type ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { detectBinary } from "../../../src/commands/onboard-helpers.js";
import type { OpenClawConfig } from "../../../src/config/config.js";

View File

@ -1,11 +1,11 @@
import {
setTopLevelChannelAllowFrom,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import {
applyAccountNameToChannelSection,
patchScopedAccountConfig,
} from "../../../src/channels/plugins/setup-helpers.js";
import {
setTopLevelChannelAllowFrom,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import type { ChannelSetupInput } from "../../../src/channels/plugins/types.core.js";
import type { DmPolicy } from "../../../src/config/types.js";

View File

@ -1,6 +1,6 @@
import type { RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk/irc";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { ircPlugin } from "./channel.js";
import type { CoreConfig } from "./types.js";
@ -27,7 +27,7 @@ function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
};
}
const ircConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const ircConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: ircPlugin,
wizard: ircPlugin.setupWizard!,
});

View File

@ -1,8 +1,8 @@
import {
resolveSetupAccountId,
setSetupChannelEnabled,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { DmPolicy } from "../../../src/config/types.js";
import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js";

View File

@ -1,6 +1,6 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/line";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import {
listLineAccountIds,
resolveDefaultLineAccountId,
@ -30,7 +30,7 @@ function createPrompter(overrides: Partial<WizardPrompter> = {}): WizardPrompter
};
}
const lineConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const lineConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: {
id: "line",
meta: { label: "LINE" },
@ -41,7 +41,7 @@ const lineConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
resolveLineAccount({ cfg, accountId: accountId ?? undefined }).config.allowFrom,
},
setup: lineSetupAdapter,
} as Parameters<typeof buildChannelSetupFlowAdapterFromSetupWizard>[0]["plugin"],
} as Parameters<typeof buildChannelSetupWizardAdapterFromSetupWizard>[0]["plugin"],
wizard: lineSetupWizard,
});

View File

@ -2,8 +2,8 @@ import {
setSetupChannelEnabled,
setTopLevelChannelDmPolicyWithAllowFrom,
splitSetupEntries,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { resolveLineAccount } from "../../../src/line/accounts.js";
import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js";

View File

@ -4,8 +4,8 @@ import {
mergeAllowFromEntries,
promptSingleChannelSecretInput,
setTopLevelChannelGroupPolicy,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import type { DmPolicy } from "../../../src/config/types.js";

View File

@ -4,8 +4,8 @@ import {
setTopLevelChannelDmPolicyWithAllowFrom,
setTopLevelChannelGroupPolicy,
splitSetupEntries,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import type { DmPolicy, MSTeamsTeamConfig } from "../../../src/config/types.js";

View File

@ -1,14 +1,14 @@
import {
applyAccountNameToChannelSection,
patchScopedAccountConfig,
} from "../../../src/channels/plugins/setup-helpers.js";
import {
mergeAllowFromEntries,
resolveSetupAccountId,
setSetupChannelEnabled,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
import {
applyAccountNameToChannelSection,
patchScopedAccountConfig,
} from "../../../src/channels/plugins/setup-helpers.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import { type ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import type { ChannelSetupInput } from "../../../src/channels/plugins/types.core.js";

View File

@ -3,8 +3,8 @@ import {
resolveSetupAccountId,
setSetupChannelEnabled,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import { type ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { ChannelSetupInput } from "../../../src/channels/plugins/types.core.js";
import type { OpenClawConfig } from "../../../src/config/config.js";

View File

@ -1,6 +1,6 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/nostr";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { WizardPrompter } from "../../../src/wizard/prompts.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { nostrPlugin } from "./channel.js";
@ -25,7 +25,7 @@ function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
};
}
const nostrConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const nostrConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: nostrPlugin,
wizard: nostrPlugin.setupWizard!,
});

View File

@ -4,8 +4,8 @@ import {
setTopLevelChannelAllowFrom,
setTopLevelChannelDmPolicyWithAllowFrom,
splitSetupEntries,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import type { OpenClawConfig } from "../../../src/config/config.js";

View File

@ -96,7 +96,7 @@ const ollamaPlugin = {
},
},
wizard: {
onboarding: {
setup: {
choiceId: "ollama",
choiceLabel: "Ollama",
choiceHint: "Cloud and local open models",

View File

@ -59,7 +59,7 @@ const sglangPlugin = {
}),
},
wizard: {
onboarding: {
setup: {
choiceId: "sglang",
choiceLabel: "SGLang",
choiceHint: "Fast self-hosted OpenAI-compatible server",

View File

@ -1,14 +1,14 @@
import {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
import {
parseSetupEntriesAllowingWildcard,
promptParsedAllowFromForScopedChannel,
setChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
import {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import { formatCliCommand } from "../../../src/cli/command-format.js";

View File

@ -3,8 +3,8 @@ import {
promptParsedAllowFromForScopedChannel,
setChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import { type ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { formatCliCommand } from "../../../src/cli/command-format.js";
import { detectBinary } from "../../../src/commands/onboard-helpers.js";

View File

@ -1,3 +1,7 @@
import {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
import {
noteChannelLookupFailure,
noteChannelLookupSummary,
@ -6,12 +10,8 @@ import {
setAccountGroupPolicyForChannel,
setLegacyChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
import {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type {
ChannelSetupWizard,
ChannelSetupWizardAllowFromEntry,

View File

@ -8,8 +8,8 @@ import {
setAccountGroupPolicyForChannel,
setLegacyChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type {
ChannelSetupWizard,
ChannelSetupWizardAllowFromEntry,

View File

@ -1,5 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import type { WizardPrompter } from "../../../src/wizard/prompts.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
@ -26,7 +26,7 @@ function createPrompter(overrides: Partial<WizardPrompter> = {}): WizardPrompter
};
}
const synologyChatConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const synologyChatConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: synologyChatPlugin,
wizard: synologyChatSetupWizard,
});

View File

@ -2,7 +2,7 @@ import {
mergeAllowFromEntries,
setSetupChannelEnabled,
splitSetupEntries,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import type { OpenClawConfig } from "../../../src/config/config.js";

View File

@ -33,7 +33,7 @@ function warnInvalidAllowFromEntries(entries: string[]) {
JSON.stringify(entry),
"- allowFrom/groupAllowFrom authorization expects numeric Telegram sender user IDs only.",
'To allow a Telegram group or supergroup, add its negative chat ID under "channels.telegram.groups" instead.',
'If you had "@username" entries, re-run onboarding (it resolves @username to IDs) or replace them manually.',
'If you had "@username" entries, re-run setup (it resolves @username to IDs) or replace them manually.',
].join(" "),
);
}

View File

@ -1,11 +1,11 @@
import {
patchChannelConfigForAccount,
splitSetupEntries,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
} from "../../../src/channels/plugins/setup-helpers.js";
import {
patchChannelConfigForAccount,
splitSetupEntries,
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import { formatCliCommand } from "../../../src/cli/command-format.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
@ -73,7 +73,7 @@ export async function promptTelegramAllowFromForAccount(params: {
cfg: OpenClawConfig;
prompter: Parameters<
NonNullable<
import("../../../src/channels/plugins/setup-flow-types.js").ChannelSetupDmPolicy["promptAllowFrom"]
import("../../../src/channels/plugins/setup-wizard-types.js").ChannelSetupDmPolicy["promptAllowFrom"]
>
>[0]["prompter"];
accountId?: string;
@ -88,7 +88,7 @@ export async function promptTelegramAllowFromForAccount(params: {
);
}
const { promptResolvedAllowFrom } =
await import("../../../src/channels/plugins/setup-flow-helpers.js");
await import("../../../src/channels/plugins/setup-wizard-helpers.js");
const unique = await promptResolvedAllowFrom({
prompter: params.prompter,
existing: resolved.config.allowFrom ?? [],

View File

@ -3,8 +3,8 @@ import {
setChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
splitSetupEntries,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import { type ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import { type ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import { type ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { hasConfiguredSecretInput } from "../../../src/config/types.secrets.js";

View File

@ -1,6 +1,6 @@
import type { OpenClawConfig, RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk/tlon";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { tlonPlugin } from "./channel.js";
@ -26,7 +26,7 @@ function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
};
}
const tlonConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const tlonConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: tlonPlugin,
wizard: tlonPlugin.setupWizard!,
});

View File

@ -2,7 +2,7 @@
* Twitch setup wizard surface for CLI setup.
*/
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import type { OpenClawConfig } from "../../../src/config/config.js";

View File

@ -59,7 +59,7 @@ const vllmPlugin = {
}),
},
wizard: {
onboarding: {
setup: {
choiceId: "vllm",
choiceLabel: "vLLM",
choiceHint: "Local/self-hosted OpenAI-compatible server",

View File

@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js";
import type { RuntimeEnv } from "../../../src/runtime.js";
import type { WizardPrompter } from "../../../src/wizard/prompts.js";
@ -83,7 +83,7 @@ function createRuntime(): RuntimeEnv {
} as unknown as RuntimeEnv;
}
const whatsappConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const whatsappConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: whatsappPlugin,
wizard: whatsappPlugin.setupWizard!,
});

View File

@ -3,8 +3,8 @@ import { loginWeb } from "../../../src/channel-web.js";
import {
normalizeAllowFromEntries,
splitSetupEntries,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import { setSetupChannelEnabled } from "../../../src/channels/plugins/setup-flow-helpers.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import { setSetupChannelEnabled } from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { formatCliCommand } from "../../../src/cli/command-format.js";
import type { OpenClawConfig } from "../../../src/config/config.js";

View File

@ -1,9 +1,9 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/zalo";
import { describe, expect, it } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { zaloPlugin } from "./channel.js";
const zaloConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const zaloConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: zaloPlugin,
wizard: zaloPlugin.setupWizard!,
});

View File

@ -1,6 +1,6 @@
import type { OpenClawConfig, RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk/zalo";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { zaloPlugin } from "./channel.js";
@ -18,7 +18,7 @@ function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
};
}
const zaloConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const zaloConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: zaloPlugin,
wizard: zaloPlugin.setupWizard!,
});

View File

@ -4,8 +4,8 @@ import {
promptSingleChannelSecretInput,
runSingleChannelSecretStep,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import type { SecretInput } from "../../../src/config/types.secrets.js";

View File

@ -1,6 +1,6 @@
import type { OpenClawConfig, WizardPrompter } from "openclaw/plugin-sdk/zalouser";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
vi.mock("./zalo-js.js", async (importOriginal) => {
@ -50,7 +50,7 @@ function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
};
}
const zalouserConfigureAdapter = buildChannelSetupFlowAdapterFromSetupWizard({
const zalouserConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: zalouserPlugin,
wizard: zalouserPlugin.setupWizard!,
});

View File

@ -1,9 +1,9 @@
import { patchScopedAccountConfig } from "../../../src/channels/plugins/setup-helpers.js";
import {
mergeAllowFromEntries,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../../../src/channels/plugins/setup-flow-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-flow-types.js";
import { patchScopedAccountConfig } from "../../../src/channels/plugins/setup-helpers.js";
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupDmPolicy } from "../../../src/channels/plugins/setup-wizard-types.js";
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { formatResolvedUnresolvedNote } from "../../../src/plugin-sdk/resolution-notes.js";

View File

@ -104,7 +104,7 @@ export function resolveAuthProfileOrder(params: {
}).eligible;
let filtered = baseOrder.filter(isValidProfile);
// Repair config/store profile-id drift from older onboarding flows:
// Repair config/store profile-id drift from older setup flows:
// if configured profile ids no longer exist in auth-profiles.json, scan the
// provider's stored credentials and use any valid entries.
const allBaseProfilesMissing = baseOrder.every((profileId) => !store.profiles[profileId]);

View File

@ -202,7 +202,7 @@ function resolveSyntheticLocalProviderAuth(params: {
// Custom providers pointing at a local server (e.g. llama.cpp, vLLM, LocalAI)
// typically don't require auth. Synthesize a local key so the auth resolver
// doesn't reject them when the user left the API key blank during onboarding.
// doesn't reject them when the user left the API key blank during setup.
if (providerConfig.baseUrl && isLocalBaseUrl(providerConfig.baseUrl)) {
return {
apiKey: CUSTOM_LOCAL_AUTH_MARKER,

View File

@ -31,22 +31,22 @@ describe("resolveDefaultAgentWorkspaceDir", () => {
const WORKSPACE_STATE_PATH_SEGMENTS = [".openclaw", "workspace-state.json"] as const;
async function readOnboardingState(dir: string): Promise<{
async function readWorkspaceState(dir: string): Promise<{
version: number;
bootstrapSeededAt?: string;
onboardingCompletedAt?: string;
setupCompletedAt?: string;
}> {
const raw = await fs.readFile(path.join(dir, ...WORKSPACE_STATE_PATH_SEGMENTS), "utf-8");
return JSON.parse(raw) as {
version: number;
bootstrapSeededAt?: string;
onboardingCompletedAt?: string;
setupCompletedAt?: string;
};
}
async function expectBootstrapSeeded(dir: string) {
await expect(fs.access(path.join(dir, DEFAULT_BOOTSTRAP_FILENAME))).resolves.toBeUndefined();
const state = await readOnboardingState(dir);
const state = await readWorkspaceState(dir);
expect(state.bootstrapSeededAt).toMatch(/\d{4}-\d{2}-\d{2}T/);
}
@ -55,8 +55,8 @@ async function expectCompletedWithoutBootstrap(dir: string) {
await expect(fs.access(path.join(dir, DEFAULT_BOOTSTRAP_FILENAME))).rejects.toMatchObject({
code: "ENOENT",
});
const state = await readOnboardingState(dir);
expect(state.onboardingCompletedAt).toMatch(/\d{4}-\d{2}-\d{2}T/);
const state = await readWorkspaceState(dir);
expect(state.setupCompletedAt).toMatch(/\d{4}-\d{2}-\d{2}T/);
}
function expectSubagentAllowedBootstrapNames(files: WorkspaceBootstrapFile[]) {
@ -78,7 +78,7 @@ describe("ensureAgentWorkspace", () => {
await ensureAgentWorkspace({ dir: tempDir, ensureBootstrapFiles: true });
await expectBootstrapSeeded(tempDir);
expect((await readOnboardingState(tempDir)).onboardingCompletedAt).toBeUndefined();
expect((await readWorkspaceState(tempDir)).setupCompletedAt).toBeUndefined();
});
it("recovers partial initialization by creating BOOTSTRAP.md when marker is missing", async () => {
@ -104,8 +104,8 @@ describe("ensureAgentWorkspace", () => {
code: "ENOENT",
});
await expect(fs.access(path.join(tempDir, DEFAULT_TOOLS_FILENAME))).resolves.toBeUndefined();
const state = await readOnboardingState(tempDir);
expect(state.onboardingCompletedAt).toMatch(/\d{4}-\d{2}-\d{2}T/);
const state = await readWorkspaceState(tempDir);
expect(state.setupCompletedAt).toMatch(/\d{4}-\d{2}-\d{2}T/);
});
it("does not re-seed BOOTSTRAP.md for legacy completed workspaces without state marker", async () => {
@ -118,9 +118,9 @@ describe("ensureAgentWorkspace", () => {
await expect(fs.access(path.join(tempDir, DEFAULT_BOOTSTRAP_FILENAME))).rejects.toMatchObject({
code: "ENOENT",
});
const state = await readOnboardingState(tempDir);
const state = await readWorkspaceState(tempDir);
expect(state.bootstrapSeededAt).toBeUndefined();
expect(state.onboardingCompletedAt).toMatch(/\d{4}-\d{2}-\d{2}T/);
expect(state.setupCompletedAt).toMatch(/\d{4}-\d{2}-\d{2}T/);
});
it("treats memory-backed workspaces as existing even when template files are missing", async () => {
@ -135,8 +135,8 @@ describe("ensureAgentWorkspace", () => {
await expect(fs.access(path.join(tempDir, DEFAULT_BOOTSTRAP_FILENAME))).rejects.toMatchObject({
code: "ENOENT",
});
const state = await readOnboardingState(tempDir);
expect(state.onboardingCompletedAt).toMatch(/\d{4}-\d{2}-\d{2}T/);
const state = await readWorkspaceState(tempDir);
expect(state.setupCompletedAt).toMatch(/\d{4}-\d{2}-\d{2}T/);
const memoryContent = await fs.readFile(path.join(tempDir, "MEMORY.md"), "utf-8");
expect(memoryContent).toBe("# Long-term memory\nImportant stuff");
});
@ -150,6 +150,28 @@ describe("ensureAgentWorkspace", () => {
await expectCompletedWithoutBootstrap(tempDir);
});
it("migrates legacy onboardingCompletedAt markers to setupCompletedAt", async () => {
const tempDir = await makeTempWorkspace("openclaw-workspace-");
await fs.mkdir(path.join(tempDir, ".openclaw"), { recursive: true });
await fs.writeFile(
path.join(tempDir, ...WORKSPACE_STATE_PATH_SEGMENTS),
JSON.stringify({
version: 1,
onboardingCompletedAt: "2026-03-15T02:30:00.000Z",
}),
);
await ensureAgentWorkspace({ dir: tempDir, ensureBootstrapFiles: true });
const state = await readWorkspaceState(tempDir);
expect(state.setupCompletedAt).toBe("2026-03-15T02:30:00.000Z");
const persisted = await fs.readFile(
path.join(tempDir, ...WORKSPACE_STATE_PATH_SEGMENTS),
"utf-8",
);
expect(persisted).toContain('"setupCompletedAt": "2026-03-15T02:30:00.000Z"');
});
});
describe("loadWorkspaceBootstrapFiles", () => {

View File

@ -159,10 +159,10 @@ export type ExtraBootstrapLoadDiagnostic = {
detail: string;
};
type WorkspaceOnboardingState = {
type WorkspaceSetupState = {
version: typeof WORKSPACE_STATE_VERSION;
bootstrapSeededAt?: string;
onboardingCompletedAt?: string;
setupCompletedAt?: string;
};
/** Set of recognized bootstrap filenames for runtime validation */
@ -207,35 +207,43 @@ function resolveWorkspaceStatePath(dir: string): string {
return path.join(dir, WORKSPACE_STATE_DIRNAME, WORKSPACE_STATE_FILENAME);
}
function parseWorkspaceOnboardingState(raw: string): WorkspaceOnboardingState | null {
function parseWorkspaceSetupState(raw: string): WorkspaceSetupState | null {
try {
const parsed = JSON.parse(raw) as {
bootstrapSeededAt?: unknown;
setupCompletedAt?: unknown;
onboardingCompletedAt?: unknown;
};
if (!parsed || typeof parsed !== "object") {
return null;
}
const legacyCompletedAt =
typeof parsed.onboardingCompletedAt === "string" ? parsed.onboardingCompletedAt : undefined;
return {
version: WORKSPACE_STATE_VERSION,
bootstrapSeededAt:
typeof parsed.bootstrapSeededAt === "string" ? parsed.bootstrapSeededAt : undefined,
onboardingCompletedAt:
typeof parsed.onboardingCompletedAt === "string" ? parsed.onboardingCompletedAt : undefined,
setupCompletedAt:
typeof parsed.setupCompletedAt === "string" ? parsed.setupCompletedAt : legacyCompletedAt,
};
} catch {
return null;
}
}
async function readWorkspaceOnboardingState(statePath: string): Promise<WorkspaceOnboardingState> {
async function readWorkspaceSetupState(statePath: string): Promise<WorkspaceSetupState> {
try {
const raw = await fs.readFile(statePath, "utf-8");
return (
parseWorkspaceOnboardingState(raw) ?? {
version: WORKSPACE_STATE_VERSION,
}
);
const parsed = parseWorkspaceSetupState(raw);
if (
parsed &&
raw.includes('"onboardingCompletedAt"') &&
!raw.includes('"setupCompletedAt"') &&
parsed.setupCompletedAt
) {
await writeWorkspaceSetupState(statePath, parsed);
}
return parsed ?? { version: WORKSPACE_STATE_VERSION };
} catch (err) {
const anyErr = err as { code?: string };
if (anyErr.code !== "ENOENT") {
@ -247,21 +255,19 @@ async function readWorkspaceOnboardingState(statePath: string): Promise<Workspac
}
}
async function readWorkspaceOnboardingStateForDir(dir: string): Promise<WorkspaceOnboardingState> {
async function readWorkspaceSetupStateForDir(dir: string): Promise<WorkspaceSetupState> {
const statePath = resolveWorkspaceStatePath(resolveUserPath(dir));
return await readWorkspaceOnboardingState(statePath);
return await readWorkspaceSetupState(statePath);
}
export async function isWorkspaceOnboardingCompleted(dir: string): Promise<boolean> {
const state = await readWorkspaceOnboardingStateForDir(dir);
return (
typeof state.onboardingCompletedAt === "string" && state.onboardingCompletedAt.trim().length > 0
);
export async function isWorkspaceSetupCompleted(dir: string): Promise<boolean> {
const state = await readWorkspaceSetupStateForDir(dir);
return typeof state.setupCompletedAt === "string" && state.setupCompletedAt.trim().length > 0;
}
async function writeWorkspaceOnboardingState(
async function writeWorkspaceSetupState(
statePath: string,
state: WorkspaceOnboardingState,
state: WorkspaceSetupState,
): Promise<void> {
await fs.mkdir(path.dirname(statePath), { recursive: true });
const payload = `${JSON.stringify(state, null, 2)}\n`;
@ -382,9 +388,9 @@ export async function ensureAgentWorkspace(params?: {
await writeFileIfMissing(userPath, userTemplate);
await writeFileIfMissing(heartbeatPath, heartbeatTemplate);
let state = await readWorkspaceOnboardingState(statePath);
let state = await readWorkspaceSetupState(statePath);
let stateDirty = false;
const markState = (next: Partial<WorkspaceOnboardingState>) => {
const markState = (next: Partial<WorkspaceSetupState>) => {
state = { ...state, ...next };
stateDirty = true;
};
@ -395,14 +401,14 @@ export async function ensureAgentWorkspace(params?: {
markState({ bootstrapSeededAt: nowIso() });
}
if (!state.onboardingCompletedAt && state.bootstrapSeededAt && !bootstrapExists) {
markState({ onboardingCompletedAt: nowIso() });
if (!state.setupCompletedAt && state.bootstrapSeededAt && !bootstrapExists) {
markState({ setupCompletedAt: nowIso() });
}
if (!state.bootstrapSeededAt && !state.onboardingCompletedAt && !bootstrapExists) {
if (!state.bootstrapSeededAt && !state.setupCompletedAt && !bootstrapExists) {
// Legacy migration path: if USER/IDENTITY diverged from templates, or if user-content
// indicators exist, treat onboarding as complete and avoid recreating BOOTSTRAP for
// already-onboarded workspaces.
// indicators exist, treat setup as complete and avoid recreating BOOTSTRAP for
// already-configured workspaces.
const [identityContent, userContent] = await Promise.all([
fs.readFile(identityPath, "utf-8"),
fs.readFile(userPath, "utf-8"),
@ -423,10 +429,10 @@ export async function ensureAgentWorkspace(params?: {
}
return false;
})();
const legacyOnboardingCompleted =
const legacySetupCompleted =
identityContent !== identityTemplate || userContent !== userTemplate || hasUserContent;
if (legacyOnboardingCompleted) {
markState({ onboardingCompletedAt: nowIso() });
if (legacySetupCompleted) {
markState({ setupCompletedAt: nowIso() });
} else {
const bootstrapTemplate = await loadTemplate(DEFAULT_BOOTSTRAP_FILENAME);
const wroteBootstrap = await writeFileIfMissing(bootstrapPath, bootstrapTemplate);
@ -442,7 +448,7 @@ export async function ensureAgentWorkspace(params?: {
}
if (stateDirty) {
await writeWorkspaceOnboardingState(statePath, state);
await writeWorkspaceSetupState(statePath, state);
}
await ensureGitRepo(dir, isBrandNewWorkspace);

View File

@ -1,5 +1,5 @@
import type { WizardPrompter } from "../../wizard/prompts.js";
import { splitSetupEntries } from "./setup-flow-helpers.js";
import { splitSetupEntries } from "./setup-wizard-helpers.js";
export type ChannelAccessPolicy = "allowlist" | "open" | "disabled";

View File

@ -1,12 +1,6 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
const promptAccountIdSdkMock = vi.hoisted(() => vi.fn(async () => "default"));
vi.mock("../../plugin-sdk/setup.js", () => ({
promptAccountId: promptAccountIdSdkMock,
}));
import {
applySingleTokenPromptResult,
buildSingleChannelSecretPromptState,
@ -35,7 +29,7 @@ import {
setLegacyChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
splitSetupEntries,
} from "./setup-flow-helpers.js";
} from "./setup-wizard-helpers.js";
function createPrompter(inputs: string[]) {
return {
@ -166,11 +160,6 @@ async function runPromptLegacyAllowFrom(params: {
}
describe("promptResolvedAllowFrom", () => {
beforeEach(() => {
promptAccountIdSdkMock.mockReset();
promptAccountIdSdkMock.mockResolvedValue("default");
});
it("re-prompts without token until all ids are parseable", async () => {
const prompter = createPrompter(["@alice", "123"]);
const resolveEntries = vi.fn();
@ -1150,11 +1139,6 @@ describe("resolveSetupAccountId", () => {
});
describe("resolveAccountIdForConfigure", () => {
beforeEach(() => {
promptAccountIdSdkMock.mockReset();
promptAccountIdSdkMock.mockResolvedValue("default");
});
it("uses normalized override without prompting", async () => {
const accountId = await resolveAccountIdForConfigure({
cfg: {},
@ -1183,12 +1167,16 @@ describe("resolveAccountIdForConfigure", () => {
});
it("prompts for account id when prompting is enabled and no override is provided", async () => {
promptAccountIdSdkMock.mockResolvedValueOnce("prompted-id");
const prompter = {
select: vi.fn(async () => "prompted-id"),
text: vi.fn(async () => ""),
note: vi.fn(async () => undefined),
};
const accountId = await resolveAccountIdForConfigure({
cfg: {},
// oxlint-disable-next-line typescript/no-explicit-any
prompter: {} as any,
prompter: prompter as any,
label: "Signal",
shouldPromptAccountIds: true,
listAccountIds: () => ["default", "prompted-id"],
@ -1196,12 +1184,12 @@ describe("resolveAccountIdForConfigure", () => {
});
expect(accountId).toBe("prompted-id");
expect(promptAccountIdSdkMock).toHaveBeenCalledWith(
expect(prompter.select).toHaveBeenCalledWith(
expect.objectContaining({
label: "Signal",
currentId: "fallback",
defaultAccountId: "fallback",
message: "Signal account",
initialValue: "fallback",
}),
);
expect(prompter.text).not.toHaveBeenCalled();
});
});

View File

@ -1,21 +1,49 @@
import {
promptSecretRefForOnboarding,
promptSecretRefForSetup,
resolveSecretInputModeForEnvSelection,
} from "../../commands/auth-choice.apply-helpers.js";
import type { OpenClawConfig } from "../../config/config.js";
import type { DmPolicy, GroupPolicy } from "../../config/types.js";
import type { SecretInput } from "../../config/types.secrets.js";
import { promptAccountId as promptAccountIdSdk } from "../../plugin-sdk/setup.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
import type { WizardPrompter } from "../../wizard/prompts.js";
import type { PromptAccountId, PromptAccountIdParams } from "./setup-flow-types.js";
import {
moveSingleAccountChannelSectionToDefaultAccount,
patchScopedAccountConfig,
} from "./setup-helpers.js";
import type { PromptAccountId, PromptAccountIdParams } from "./setup-wizard-types.js";
export const promptAccountId: PromptAccountId = async (params: PromptAccountIdParams) => {
return await promptAccountIdSdk(params);
const existingIds = params.listAccountIds(params.cfg);
const initial = params.currentId?.trim() || params.defaultAccountId || DEFAULT_ACCOUNT_ID;
const choice = await params.prompter.select({
message: `${params.label} account`,
options: [
...existingIds.map((id) => ({
value: id,
label: id === DEFAULT_ACCOUNT_ID ? "default (primary)" : id,
})),
{ value: "__new__", label: "Add a new account" },
],
initialValue: initial,
});
if (choice !== "__new__") {
return normalizeAccountId(choice);
}
const entered = await params.prompter.text({
message: `New ${params.label} account id`,
validate: (value) => (value?.trim() ? undefined : "Required"),
});
const normalized = normalizeAccountId(String(entered));
if (String(entered).trim() !== normalized) {
await params.prompter.note(
`Normalized account id to "${normalized}".`,
`${params.label} account`,
);
}
return normalized;
};
export function addWildcardAllowFrom(allowFrom?: Array<string | number> | null): string[] {
@ -617,7 +645,7 @@ export async function promptSingleChannelSecretInput(params: {
}
}
const resolved = await promptSecretRefForOnboarding({
const resolved = await promptSecretRefForSetup({
provider: params.providerHint,
config: params.cfg,
prompter: params.prompter as WizardPrompter,

View File

@ -90,7 +90,7 @@ export type ChannelSetupDmPolicy = {
}) => Promise<OpenClawConfig>;
};
export type ChannelSetupFlowAdapter = {
export type ChannelSetupWizardAdapter = {
channel: ChannelId;
getStatus: (ctx: ChannelSetupStatusContext) => Promise<ChannelSetupStatus>;
configure: (ctx: ChannelSetupConfigureContext) => Promise<ChannelSetupResult>;

View File

@ -1,21 +1,21 @@
import type { OpenClawConfig } from "../../config/config.js";
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
import type { WizardPrompter } from "../../wizard/prompts.js";
import { configureChannelAccessWithAllowlist } from "./setup-group-access-configure.js";
import type { ChannelAccessPolicy } from "./setup-group-access.js";
import {
promptResolvedAllowFrom,
resolveAccountIdForConfigure,
runSingleChannelSecretStep,
splitSetupEntries,
} from "./setup-flow-helpers.js";
} from "./setup-wizard-helpers.js";
import type {
ChannelSetupFlowAdapter,
ChannelSetupWizardAdapter,
ChannelSetupConfigureContext,
ChannelSetupDmPolicy,
ChannelSetupStatus,
ChannelSetupStatusContext,
} from "./setup-flow-types.js";
import { configureChannelAccessWithAllowlist } from "./setup-group-access-configure.js";
import type { ChannelAccessPolicy } from "./setup-group-access.js";
} from "./setup-wizard-types.js";
import type { ChannelSetupInput } from "./types.core.js";
import type { ChannelPlugin } from "./types.js";
@ -273,7 +273,7 @@ export type ChannelSetupWizard = {
allowFrom?: ChannelSetupWizardAllowFrom;
groupAccess?: ChannelSetupWizardGroupAccess;
disable?: (cfg: OpenClawConfig) => OpenClawConfig;
onAccountRecorded?: ChannelSetupFlowAdapter["onAccountRecorded"];
onAccountRecorded?: ChannelSetupWizardAdapter["onAccountRecorded"];
};
type ChannelSetupWizardPlugin = Pick<ChannelPlugin, "id" | "meta" | "config" | "setup">;
@ -399,10 +399,10 @@ async function applyWizardTextInputValue(params: {
}).cfg;
}
export function buildChannelSetupFlowAdapterFromSetupWizard(params: {
export function buildChannelSetupWizardAdapterFromSetupWizard(params: {
plugin: ChannelSetupWizardPlugin;
wizard: ChannelSetupWizard;
}): ChannelSetupFlowAdapter {
}): ChannelSetupWizardAdapter {
const { plugin, wizard } = params;
return {
channel: plugin.id,

View File

@ -56,7 +56,7 @@ const coreEntries: CoreCliEntry[] = [
commands: [
{
name: "onboard",
description: "Interactive onboarding wizard for gateway, workspace, and skills",
description: "Interactive setup wizard for gateway, workspace, and skills",
hasSubcommands: false,
},
],

View File

@ -12,7 +12,7 @@ export const CORE_CLI_COMMAND_DESCRIPTORS = [
},
{
name: "onboard",
description: "Interactive onboarding wizard for gateway, workspace, and skills",
description: "Interactive setup wizard for gateway, workspace, and skills",
hasSubcommands: false,
},
{

View File

@ -20,7 +20,7 @@ export function registerSetupCommand(program: Command) {
"--workspace <dir>",
"Agent workspace directory (default: ~/.openclaw/workspace; stored as agents.defaults.workspace)",
)
.option("--wizard", "Run the interactive onboarding wizard", false)
.option("--wizard", "Run the interactive setup wizard", false)
.option("--non-interactive", "Run the wizard without prompts", false)
.option("--mode <mode>", "Wizard mode: local|remote")
.option("--remote-url <url>", "Remote Gateway WebSocket URL")

View File

@ -282,7 +282,7 @@ describe("ensureApiKeyFromEnvOrPrompt", () => {
setCredential,
}),
).rejects.toThrow(
'Environment variable "MINIMAX_API_KEY" is required for --secret-input-mode ref in non-interactive onboarding.',
'Environment variable "MINIMAX_API_KEY" is required for --secret-input-mode ref in non-interactive setup.',
);
expect(setCredential).not.toHaveBeenCalled();
});

View File

@ -32,7 +32,7 @@ export type SecretInputModePromptCopy = {
refHint?: string;
};
export type SecretRefOnboardingPromptCopy = {
export type SecretRefSetupPromptCopy = {
sourceMessage?: string;
envVarMessage?: string;
envVarPlaceholder?: string;
@ -72,13 +72,13 @@ function resolveRefFallbackInput(params: {
const fallbackEnvVar = params.preferredEnvVar ?? resolveDefaultProviderEnvVar(params.provider);
if (!fallbackEnvVar) {
throw new Error(
`No default environment variable mapping found for provider "${params.provider}". Set a provider-specific env var, or re-run onboarding in an interactive terminal to configure a ref.`,
`No default environment variable mapping found for provider "${params.provider}". Set a provider-specific env var, or re-run setup in an interactive terminal to configure a ref.`,
);
}
const value = process.env[fallbackEnvVar]?.trim();
if (!value) {
throw new Error(
`Environment variable "${fallbackEnvVar}" is required for --secret-input-mode ref in non-interactive onboarding.`,
`Environment variable "${fallbackEnvVar}" is required for --secret-input-mode ref in non-interactive setup.`,
);
}
return {
@ -93,12 +93,12 @@ function resolveRefFallbackInput(params: {
};
}
export async function promptSecretRefForOnboarding(params: {
export async function promptSecretRefForSetup(params: {
provider: string;
config: OpenClawConfig;
prompter: WizardPrompter;
preferredEnvVar?: string;
copy?: SecretRefOnboardingPromptCopy;
copy?: SecretRefSetupPromptCopy;
}): Promise<{ ref: SecretRef; resolvedValue: string }> {
const defaultEnvVar =
params.preferredEnvVar ?? resolveDefaultProviderEnvVar(params.provider) ?? "";
@ -506,7 +506,7 @@ export async function ensureApiKeyFromEnvOrPrompt(params: {
await params.setCredential(fallback.ref, selectedMode);
return fallback.resolvedValue;
}
const resolved = await promptSecretRefForOnboarding({
const resolved = await promptSecretRefForSetup({
provider: params.provider,
config: params.config,
prompter: params.prompter,

View File

@ -3,7 +3,7 @@ import { normalizeApiKeyInput, validateApiKeyInput } from "./auth-choice.api-key
import {
normalizeSecretInputModeInput,
ensureApiKeyFromOptionEnvOrPrompt,
promptSecretRefForOnboarding,
promptSecretRefForSetup,
resolveSecretInputModeForEnvSelection,
} from "./auth-choice.apply-helpers.js";
import type { ApplyAuthChoiceParams, ApplyAuthChoiceResult } from "./auth-choice.apply.js";
@ -42,7 +42,7 @@ export async function applyAuthChoiceAnthropic(
let token = "";
let tokenRef: { source: "env" | "file" | "exec"; provider: string; id: string } | undefined;
if (selectedMode === "ref") {
const resolved = await promptSecretRefForOnboarding({
const resolved = await promptSecretRefForSetup({
provider: "anthropic-setup-token",
config: params.config,
prompter: params.prompter,

View File

@ -90,7 +90,7 @@ export async function applyAuthChoiceOpenAI(
});
} catch {
// The helper already surfaces the error to the user.
// Keep onboarding flow alive and return unchanged config.
// Keep setup flow alive and return unchanged config.
return { config: nextConfig, agentModelOverride };
}
if (creds) {

View File

@ -61,12 +61,12 @@ import { loadOpenClawPlugins } from "../../plugins/loader.js";
import { createEmptyPluginRegistry } from "../../plugins/registry.js";
import { setActivePluginRegistry } from "../../plugins/runtime.js";
import type { WizardPrompter } from "../../wizard/prompts.js";
import { makePrompter, makeRuntime } from "./__tests__/test-utils.js";
import { makePrompter, makeRuntime } from "../setup/__tests__/test-utils.js";
import {
ensureOnboardingPluginInstalled,
loadOnboardingPluginRegistrySnapshotForChannel,
reloadOnboardingPluginRegistry,
reloadOnboardingPluginRegistryForChannel,
ensureChannelSetupPluginInstalled,
loadChannelSetupPluginRegistrySnapshotForChannel,
reloadChannelSetupPluginRegistry,
reloadChannelSetupPluginRegistryForChannel,
} from "./plugin-install.js";
const baseEntry: ChannelPluginCatalogEntry = {
@ -106,7 +106,7 @@ async function runInitialValueForChannel(channel: "dev" | "beta") {
const cfg: OpenClawConfig = { update: { channel } };
mockRepoLocalPathExists();
await ensureOnboardingPluginInstalled({
await ensureChannelSetupPluginInstalled({
cfg,
entry: baseEntry,
prompter,
@ -118,14 +118,14 @@ async function runInitialValueForChannel(channel: "dev" | "beta") {
}
function expectPluginLoadedFromLocalPath(
result: Awaited<ReturnType<typeof ensureOnboardingPluginInstalled>>,
result: Awaited<ReturnType<typeof ensureChannelSetupPluginInstalled>>,
) {
const expectedPath = path.resolve(process.cwd(), "extensions/zalo");
expect(result.installed).toBe(true);
expect(result.cfg.plugins?.load?.paths).toContain(expectedPath);
}
describe("ensureOnboardingPluginInstalled", () => {
describe("ensureChannelSetupPluginInstalled", () => {
it("installs from npm and enables the plugin", async () => {
const runtime = makeRuntime();
const prompter = makePrompter({
@ -140,7 +140,7 @@ describe("ensureOnboardingPluginInstalled", () => {
extensions: [],
});
const result = await ensureOnboardingPluginInstalled({
const result = await ensureChannelSetupPluginInstalled({
cfg,
entry: baseEntry,
prompter,
@ -166,7 +166,7 @@ describe("ensureOnboardingPluginInstalled", () => {
const cfg: OpenClawConfig = {};
mockRepoLocalPathExists();
const result = await ensureOnboardingPluginInstalled({
const result = await ensureChannelSetupPluginInstalled({
cfg,
entry: baseEntry,
prompter,
@ -185,7 +185,7 @@ describe("ensureOnboardingPluginInstalled", () => {
const cfg: OpenClawConfig = {};
mockRepoLocalPathExists();
const result = await ensureOnboardingPluginInstalled({
const result = await ensureChannelSetupPluginInstalled({
cfg,
entry: {
...baseEntry,
@ -228,7 +228,7 @@ describe("ensureOnboardingPluginInstalled", () => {
]),
);
await ensureOnboardingPluginInstalled({
await ensureChannelSetupPluginInstalled({
cfg,
entry: baseEntry,
prompter,
@ -264,7 +264,7 @@ describe("ensureOnboardingPluginInstalled", () => {
error: "nope",
});
const result = await ensureOnboardingPluginInstalled({
const result = await ensureChannelSetupPluginInstalled({
cfg,
entry: baseEntry,
prompter,
@ -280,7 +280,7 @@ describe("ensureOnboardingPluginInstalled", () => {
const runtime = makeRuntime();
const cfg: OpenClawConfig = {};
reloadOnboardingPluginRegistry({
reloadChannelSetupPluginRegistry({
cfg,
runtime,
workspaceDir: "/tmp/openclaw-workspace",
@ -300,11 +300,11 @@ describe("ensureOnboardingPluginInstalled", () => {
);
});
it("scopes channel reloads when onboarding starts from an empty registry", () => {
it("scopes channel reloads when setup starts from an empty registry", () => {
const runtime = makeRuntime();
const cfg: OpenClawConfig = {};
reloadOnboardingPluginRegistryForChannel({
reloadChannelSetupPluginRegistryForChannel({
cfg,
runtime,
channel: "telegram",
@ -348,7 +348,7 @@ describe("ensureOnboardingPluginInstalled", () => {
});
setActivePluginRegistry(registry);
reloadOnboardingPluginRegistryForChannel({
reloadChannelSetupPluginRegistryForChannel({
cfg,
runtime,
channel: "telegram",
@ -366,7 +366,7 @@ describe("ensureOnboardingPluginInstalled", () => {
const runtime = makeRuntime();
const cfg: OpenClawConfig = {};
loadOnboardingPluginRegistrySnapshotForChannel({
loadChannelSetupPluginRegistrySnapshotForChannel({
cfg,
runtime,
channel: "telegram",
@ -389,7 +389,7 @@ describe("ensureOnboardingPluginInstalled", () => {
const runtime = makeRuntime();
const cfg: OpenClawConfig = {};
loadOnboardingPluginRegistrySnapshotForChannel({
loadChannelSetupPluginRegistrySnapshotForChannel({
cfg,
runtime,
channel: "msteams",

View File

@ -139,7 +139,7 @@ function resolveInstallDefaultChoice(params: {
return localPath ? "local" : "npm";
}
export async function ensureOnboardingPluginInstalled(params: {
export async function ensureChannelSetupPluginInstalled(params: {
cfg: OpenClawConfig;
entry: ChannelPluginCatalogEntry;
prompter: WizardPrompter;
@ -225,15 +225,15 @@ export async function ensureOnboardingPluginInstalled(params: {
return { cfg: next, installed: false };
}
export function reloadOnboardingPluginRegistry(params: {
export function reloadChannelSetupPluginRegistry(params: {
cfg: OpenClawConfig;
runtime: RuntimeEnv;
workspaceDir?: string;
}): void {
loadOnboardingPluginRegistry(params);
loadChannelSetupPluginRegistry(params);
}
function loadOnboardingPluginRegistry(params: {
function loadChannelSetupPluginRegistry(params: {
cfg: OpenClawConfig;
runtime: RuntimeEnv;
workspaceDir?: string;
@ -255,7 +255,7 @@ function loadOnboardingPluginRegistry(params: {
});
}
export function reloadOnboardingPluginRegistryForChannel(params: {
export function reloadChannelSetupPluginRegistryForChannel(params: {
cfg: OpenClawConfig;
runtime: RuntimeEnv;
channel: string;
@ -264,24 +264,24 @@ export function reloadOnboardingPluginRegistryForChannel(params: {
}): void {
const activeRegistry = getActivePluginRegistry();
// On low-memory hosts, the empty-registry fallback should only recover the selected
// plugin instead of importing every bundled extension during onboarding.
// plugin instead of importing every bundled extension during setup.
const onlyPluginIds = activeRegistry?.plugins.length
? undefined
: [params.pluginId ?? params.channel];
loadOnboardingPluginRegistry({
loadChannelSetupPluginRegistry({
...params,
onlyPluginIds,
});
}
export function loadOnboardingPluginRegistrySnapshotForChannel(params: {
export function loadChannelSetupPluginRegistrySnapshotForChannel(params: {
cfg: OpenClawConfig;
runtime: RuntimeEnv;
channel: string;
pluginId?: string;
workspaceDir?: string;
}): PluginRegistry {
return loadOnboardingPluginRegistry({
return loadChannelSetupPluginRegistry({
...params,
onlyPluginIds: [params.pluginId ?? params.channel],
activate: false,

View File

@ -1,20 +1,20 @@
import { listChannelSetupPlugins } from "../../channels/plugins/setup-registry.js";
import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../channels/plugins/setup-wizard.js";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../channels/plugins/setup-wizard.js";
import type { ChannelPlugin } from "../../channels/plugins/types.js";
import type { ChannelChoice } from "../onboard-types.js";
import type { ChannelSetupFlowAdapter } from "./types.js";
import type { ChannelSetupWizardAdapter } from "./types.js";
const setupWizardAdapters = new WeakMap<object, ChannelSetupFlowAdapter>();
const setupWizardAdapters = new WeakMap<object, ChannelSetupWizardAdapter>();
export function resolveChannelSetupFlowAdapterForPlugin(
export function resolveChannelSetupWizardAdapterForPlugin(
plugin?: ChannelPlugin,
): ChannelSetupFlowAdapter | undefined {
): ChannelSetupWizardAdapter | undefined {
if (plugin?.setupWizard) {
const cached = setupWizardAdapters.get(plugin);
if (cached) {
return cached;
}
const adapter = buildChannelSetupFlowAdapterFromSetupWizard({
const adapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin,
wizard: plugin.setupWizard,
});
@ -24,10 +24,10 @@ export function resolveChannelSetupFlowAdapterForPlugin(
return undefined;
}
const CHANNEL_SETUP_FLOW_ADAPTERS = () => {
const adapters = new Map<ChannelChoice, ChannelSetupFlowAdapter>();
const getChannelSetupWizardAdapterMap = () => {
const adapters = new Map<ChannelChoice, ChannelSetupWizardAdapter>();
for (const plugin of listChannelSetupPlugins()) {
const adapter = resolveChannelSetupFlowAdapterForPlugin(plugin);
const adapter = resolveChannelSetupWizardAdapterForPlugin(plugin);
if (!adapter) {
continue;
}
@ -36,12 +36,12 @@ const CHANNEL_SETUP_FLOW_ADAPTERS = () => {
return adapters;
};
export function getChannelSetupFlowAdapter(
export function getChannelSetupWizardAdapter(
channel: ChannelChoice,
): ChannelSetupFlowAdapter | undefined {
return CHANNEL_SETUP_FLOW_ADAPTERS().get(channel);
): ChannelSetupWizardAdapter | undefined {
return getChannelSetupWizardAdapterMap().get(channel);
}
export function listChannelSetupFlowAdapters(): ChannelSetupFlowAdapter[] {
return Array.from(CHANNEL_SETUP_FLOW_ADAPTERS().values());
export function listChannelSetupWizardAdapters(): ChannelSetupWizardAdapter[] {
return Array.from(getChannelSetupWizardAdapterMap().values());
}

View File

@ -1 +1 @@
export * from "../../channels/plugins/setup-flow-types.js";
export * from "../../channels/plugins/setup-wizard-types.js";

View File

@ -6,22 +6,22 @@ import { telegramPlugin } from "../../extensions/telegram/src/channel.js";
import { whatsappPlugin } from "../../extensions/whatsapp/src/channel.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import { createTestRegistry } from "../test-utils/channel-plugins.js";
import { getChannelSetupFlowAdapter } from "./channel-setup/registry.js";
import type { ChannelSetupFlowAdapter } from "./channel-setup/types.js";
import { getChannelSetupWizardAdapter } from "./channel-setup/registry.js";
import type { ChannelSetupWizardAdapter } from "./channel-setup/types.js";
import type { ChannelChoice } from "./onboard-types.js";
type ChannelSetupFlowAdapterPatch = Partial<
type ChannelSetupWizardAdapterPatch = Partial<
Pick<
ChannelSetupFlowAdapter,
ChannelSetupWizardAdapter,
"configure" | "configureInteractive" | "configureWhenConfigured" | "getStatus"
>
>;
type PatchedSetupAdapterFields = {
configure?: ChannelSetupFlowAdapter["configure"];
configureInteractive?: ChannelSetupFlowAdapter["configureInteractive"];
configureWhenConfigured?: ChannelSetupFlowAdapter["configureWhenConfigured"];
getStatus?: ChannelSetupFlowAdapter["getStatus"];
configure?: ChannelSetupWizardAdapter["configure"];
configureInteractive?: ChannelSetupWizardAdapter["configureInteractive"];
configureWhenConfigured?: ChannelSetupWizardAdapter["configureWhenConfigured"];
getStatus?: ChannelSetupWizardAdapter["getStatus"];
};
export function setDefaultChannelPluginRegistryForTests(): void {
@ -36,11 +36,11 @@ export function setDefaultChannelPluginRegistryForTests(): void {
setActivePluginRegistry(createTestRegistry(channels));
}
export function patchChannelSetupFlowAdapter(
export function patchChannelSetupWizardAdapter(
channel: ChannelChoice,
patch: ChannelSetupFlowAdapterPatch,
patch: ChannelSetupWizardAdapterPatch,
): () => void {
const adapter = getChannelSetupFlowAdapter(channel);
const adapter = getChannelSetupWizardAdapter(channel);
if (!adapter) {
throw new Error(`missing setup adapter for ${channel}`);
}

View File

@ -2,12 +2,12 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { ChannelPluginCatalogEntry } from "../channels/plugins/catalog.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
import {
ensureChannelSetupPluginInstalled,
loadChannelSetupPluginRegistrySnapshotForChannel,
} from "./channel-setup/plugin-install.js";
import { setDefaultChannelPluginRegistryForTests } from "./channel-test-helpers.js";
import { configMocks, offsetMocks } from "./channels.mock-harness.js";
import {
ensureOnboardingPluginInstalled,
loadOnboardingPluginRegistrySnapshotForChannel,
} from "./onboarding/plugin-install.js";
import { baseConfigSnapshot, createTestRuntime } from "./test-runtime-config-helpers.js";
const catalogMocks = vi.hoisted(() => ({
@ -34,12 +34,12 @@ vi.mock("../plugins/manifest-registry.js", async (importOriginal) => {
};
});
vi.mock("./onboarding/plugin-install.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./onboarding/plugin-install.js")>();
vi.mock("./channel-setup/plugin-install.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./channel-setup/plugin-install.js")>();
return {
...actual,
ensureOnboardingPluginInstalled: vi.fn(async ({ cfg }) => ({ cfg, installed: true })),
loadOnboardingPluginRegistrySnapshotForChannel: vi.fn(() => createTestRegistry()),
ensureChannelSetupPluginInstalled: vi.fn(async ({ cfg }) => ({ cfg, installed: true })),
loadChannelSetupPluginRegistrySnapshotForChannel: vi.fn(() => createTestRegistry()),
};
});
@ -65,13 +65,15 @@ describe("channelsAddCommand", () => {
plugins: [],
diagnostics: [],
});
vi.mocked(ensureOnboardingPluginInstalled).mockClear();
vi.mocked(ensureOnboardingPluginInstalled).mockImplementation(async ({ cfg }) => ({
vi.mocked(ensureChannelSetupPluginInstalled).mockClear();
vi.mocked(ensureChannelSetupPluginInstalled).mockImplementation(async ({ cfg }) => ({
cfg,
installed: true,
}));
vi.mocked(loadOnboardingPluginRegistrySnapshotForChannel).mockClear();
vi.mocked(loadOnboardingPluginRegistrySnapshotForChannel).mockReturnValue(createTestRegistry());
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockClear();
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockReturnValue(
createTestRegistry(),
);
setDefaultChannelPluginRegistryForTests();
});
@ -151,7 +153,7 @@ describe("channelsAddCommand", () => {
})),
},
};
vi.mocked(loadOnboardingPluginRegistrySnapshotForChannel).mockReturnValue(
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockReturnValue(
createTestRegistry([{ pluginId: "msteams", plugin: scopedMSTeamsPlugin, source: "test" }]),
);
@ -165,10 +167,10 @@ describe("channelsAddCommand", () => {
{ hasFlags: true },
);
expect(ensureOnboardingPluginInstalled).toHaveBeenCalledWith(
expect(ensureChannelSetupPluginInstalled).toHaveBeenCalledWith(
expect.objectContaining({ entry: catalogEntry }),
);
expect(loadOnboardingPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect(loadChannelSetupPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect.objectContaining({
channel: "msteams",
pluginId: "@openclaw/msteams-plugin",
@ -234,7 +236,7 @@ describe("channelsAddCommand", () => {
})),
},
};
vi.mocked(loadOnboardingPluginRegistrySnapshotForChannel).mockReturnValue(
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockReturnValue(
createTestRegistry([{ pluginId: "msteams", plugin: scopedMSTeamsPlugin, source: "test" }]),
);
@ -248,8 +250,8 @@ describe("channelsAddCommand", () => {
{ hasFlags: true },
);
expect(ensureOnboardingPluginInstalled).not.toHaveBeenCalled();
expect(loadOnboardingPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect(ensureChannelSetupPluginInstalled).not.toHaveBeenCalled();
expect(loadChannelSetupPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect.objectContaining({
channel: "msteams",
pluginId: "@openclaw/msteams-plugin",
@ -285,12 +287,12 @@ describe("channelsAddCommand", () => {
},
};
catalogMocks.listChannelPluginCatalogEntries.mockReturnValue([catalogEntry]);
vi.mocked(ensureOnboardingPluginInstalled).mockImplementation(async ({ cfg }) => ({
vi.mocked(ensureChannelSetupPluginInstalled).mockImplementation(async ({ cfg }) => ({
cfg,
installed: true,
pluginId: "@vendor/teams-runtime",
}));
vi.mocked(loadOnboardingPluginRegistrySnapshotForChannel).mockReturnValue(
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockReturnValue(
createTestRegistry([
{
pluginId: "@vendor/teams-runtime",
@ -328,7 +330,7 @@ describe("channelsAddCommand", () => {
{ hasFlags: true },
);
expect(loadOnboardingPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect(loadChannelSetupPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect.objectContaining({
channel: "msteams",
pluginId: "@vendor/teams-runtime",

View File

@ -2,8 +2,8 @@ import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/ag
import { listChannelPluginCatalogEntries } from "../../channels/plugins/catalog.js";
import { parseOptionalDelimitedEntries } from "../../channels/plugins/helpers.js";
import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
import type { ChannelSetupPlugin } from "../../channels/plugins/setup-flow-types.js";
import { moveSingleAccountChannelSectionToDefaultAccount } from "../../channels/plugins/setup-helpers.js";
import type { ChannelSetupPlugin } from "../../channels/plugins/setup-wizard-types.js";
import type { ChannelId, ChannelPlugin, ChannelSetupInput } from "../../channels/plugins/types.js";
import { writeConfigFile, type OpenClawConfig } from "../../config/config.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
@ -188,9 +188,9 @@ export async function channelsAddCommand(
if (existing) {
return existing;
}
const { loadOnboardingPluginRegistrySnapshotForChannel } =
await import("../onboarding/plugin-install.js");
const snapshot = loadOnboardingPluginRegistrySnapshotForChannel({
const { loadChannelSetupPluginRegistrySnapshotForChannel } =
await import("../channel-setup/plugin-install.js");
const snapshot = loadChannelSetupPluginRegistrySnapshotForChannel({
cfg: nextConfig,
runtime,
channel: channelId,
@ -212,9 +212,10 @@ export async function channelsAddCommand(
workspaceDir,
})
) {
const { ensureOnboardingPluginInstalled } = await import("../onboarding/plugin-install.js");
const { ensureChannelSetupPluginInstalled } =
await import("../channel-setup/plugin-install.js");
const prompter = createClackPrompter();
const result = await ensureOnboardingPluginInstalled({
const result = await ensureChannelSetupPluginInstalled({
cfg: nextConfig,
entry: catalogEntry,
prompter,

View File

@ -10,8 +10,8 @@ import { defaultRuntime } from "../runtime.js";
import { note } from "../terminal/note.js";
import { resolveUserPath } from "../utils.js";
import { createClackPrompter } from "../wizard/clack-prompter.js";
import { resolveOnboardingSecretInputString } from "../wizard/onboarding.secret-input.js";
import { WizardCancelledError } from "../wizard/prompts.js";
import { resolveSetupSecretInputString } from "../wizard/setup.secret-input.js";
import { removeChannelConfigWizard } from "./configure.channels.js";
import { maybeInstallDaemon } from "./configure.daemon.js";
import { promptAuthConfig } from "./configure.gateway-auth.js";
@ -54,7 +54,7 @@ async function resolveGatewaySecretInputForWizard(params: {
path: string;
}): Promise<string | undefined> {
try {
return await resolveOnboardingSecretInputString({
return await resolveSetupSecretInputString({
config: params.cfg,
value: params.value,
path: params.path,

View File

@ -163,7 +163,7 @@ export async function doctorShellCompletion(
}
/**
* Ensure completion cache exists. Used during onboarding/update to fix
* Ensure completion cache exists. Used during setup/update to fix
* cases where profile has completion but no cache.
* This is a silent fix - no prompts.
*/

View File

@ -126,7 +126,7 @@ export function noteOpencodeProviderOverrides(cfg: OpenClawConfig): void {
});
lines.push(
"- Remove these entries to restore per-model API routing + costs (then re-run onboarding if needed).",
"- Remove these entries to restore per-model API routing + costs (then re-run setup if needed).",
);
note(lines.join("\n"), "OpenCode");
}

View File

@ -6,7 +6,7 @@ import {
promptDefaultModel,
promptModelAllowlist,
} from "./model-picker.js";
import { makePrompter } from "./onboarding/__tests__/test-utils.js";
import { makePrompter } from "./setup/__tests__/test-utils.js";
const loadModelCatalog = vi.hoisted(() => vi.fn());
vi.mock("../agents/model-catalog.js", () => ({
@ -76,7 +76,7 @@ beforeEach(() => {
});
describe("promptDefaultModel", () => {
it("supports configuring vLLM during onboarding", async () => {
it("supports configuring vLLM during setup", async () => {
loadModelCatalog.mockResolvedValue([
{
provider: "anthropic",

View File

@ -313,7 +313,7 @@ export async function promptAndConfigureOllama(params: {
`Ollama could not be reached at ${baseUrl}.`,
"Download it at https://ollama.com/download",
"",
"Start Ollama and re-run onboarding.",
"Start Ollama and re-run setup.",
].join("\n"),
"Ollama",
);
@ -486,7 +486,7 @@ export async function configureOllamaNonInteractive(params: {
runtime.error(
[
`No Ollama models are available at ${baseUrl}.`,
"Pull a model first, then re-run onboarding.",
"Pull a model first, then re-run setup.",
].join("\n"),
);
runtime.exit(1);

View File

@ -338,7 +338,7 @@ export function applyVeniceProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
/**
* Apply Venice provider configuration AND set Venice as the default model.
* Use this when Venice is the primary provider choice during onboarding.
* Use this when Venice is the primary provider choice during setup.
*/
export function applyVeniceConfig(cfg: OpenClawConfig): OpenClawConfig {
const next = applyVeniceProviderConfig(cfg);
@ -368,7 +368,7 @@ export function applyTogetherProviderConfig(cfg: OpenClawConfig): OpenClawConfig
/**
* Apply Together provider configuration AND set Together as the default model.
* Use this when Together is the primary provider choice during onboarding.
* Use this when Together is the primary provider choice during setup.
*/
export function applyTogetherConfig(cfg: OpenClawConfig): OpenClawConfig {
const next = applyTogetherProviderConfig(cfg);
@ -477,7 +477,7 @@ export function applyKilocodeProviderConfig(cfg: OpenClawConfig): OpenClawConfig
/**
* Apply Kilo Gateway provider configuration AND set Kilo Gateway as the default model.
* Use this when Kilo Gateway is the primary provider choice during onboarding.
* Use this when Kilo Gateway is the primary provider choice during setup.
*/
export function applyKilocodeConfig(cfg: OpenClawConfig): OpenClawConfig {
const next = applyKilocodeProviderConfig(cfg);

View File

@ -467,7 +467,7 @@ describe("applyZaiConfig", () => {
it("adds zai provider with correct settings", () => {
const cfg = applyZaiConfig({});
expect(cfg.models?.providers?.zai).toMatchObject({
// Default: general (non-coding) endpoint. Coding Plan endpoint is detected during onboarding.
// Default: general (non-coding) endpoint. Coding Plan endpoint is detected during setup.
baseUrl: ZAI_GLOBAL_BASE_URL,
api: "openai-completions",
});

View File

@ -5,15 +5,15 @@ import { createEmptyPluginRegistry } from "../plugins/registry.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import {
patchChannelSetupFlowAdapter,
ensureChannelSetupPluginInstalled,
loadChannelSetupPluginRegistrySnapshotForChannel,
reloadChannelSetupPluginRegistry,
} from "./channel-setup/plugin-install.js";
import {
patchChannelSetupWizardAdapter,
setDefaultChannelPluginRegistryForTests,
} from "./channel-test-helpers.js";
import { setupChannels } from "./onboard-channels.js";
import {
ensureOnboardingPluginInstalled,
loadOnboardingPluginRegistrySnapshotForChannel,
reloadOnboardingPluginRegistry,
} from "./onboarding/plugin-install.js";
import { createExitThrowingRuntime, createWizardPrompter } from "./test-wizard-helpers.js";
const catalogMocks = vi.hoisted(() => ({
@ -96,8 +96,8 @@ function createTelegramCfg(botToken: string, enabled?: boolean): OpenClawConfig
} as OpenClawConfig;
}
function patchTelegramAdapter(overrides: Parameters<typeof patchChannelSetupFlowAdapter>[1]) {
return patchChannelSetupFlowAdapter("telegram", {
function patchTelegramAdapter(overrides: Parameters<typeof patchChannelSetupWizardAdapter>[1]) {
return patchChannelSetupWizardAdapter("telegram", {
...overrides,
getStatus:
overrides.getStatus ??
@ -214,17 +214,17 @@ vi.mock("./onboard-helpers.js", () => ({
detectBinary: vi.fn(async () => false),
}));
vi.mock("./onboarding/plugin-install.js", async (importOriginal) => {
vi.mock("./channel-setup/plugin-install.js", async (importOriginal) => {
const actual = await importOriginal();
return {
...(actual as Record<string, unknown>),
ensureOnboardingPluginInstalled: vi.fn(async ({ cfg }: { cfg: OpenClawConfig }) => ({
ensureChannelSetupPluginInstalled: vi.fn(async ({ cfg }: { cfg: OpenClawConfig }) => ({
cfg,
installed: true,
})),
// Allow tests to simulate an empty plugin registry during onboarding.
loadOnboardingPluginRegistrySnapshotForChannel: vi.fn(() => createEmptyPluginRegistry()),
reloadOnboardingPluginRegistry: vi.fn(() => {}),
// Allow tests to simulate an empty plugin registry during setup.
loadChannelSetupPluginRegistrySnapshotForChannel: vi.fn(() => createEmptyPluginRegistry()),
reloadChannelSetupPluginRegistry: vi.fn(() => {}),
};
});
@ -237,13 +237,13 @@ describe("setupChannels", () => {
plugins: [],
diagnostics: [],
});
vi.mocked(ensureOnboardingPluginInstalled).mockClear();
vi.mocked(ensureOnboardingPluginInstalled).mockImplementation(async ({ cfg }) => ({
vi.mocked(ensureChannelSetupPluginInstalled).mockClear();
vi.mocked(ensureChannelSetupPluginInstalled).mockImplementation(async ({ cfg }) => ({
cfg,
installed: true,
}));
vi.mocked(loadOnboardingPluginRegistrySnapshotForChannel).mockClear();
vi.mocked(reloadOnboardingPluginRegistry).mockClear();
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockClear();
vi.mocked(reloadChannelSetupPluginRegistry).mockClear();
});
it("QuickStart uses single-select (no multiselect) and doesn't prompt for Telegram token when WhatsApp is chosen", async () => {
const select = vi.fn(async () => "whatsapp");
@ -311,8 +311,8 @@ describe("setupChannels", () => {
);
});
expect(sawHardStop).toBe(false);
expect(loadOnboardingPluginRegistrySnapshotForChannel).not.toHaveBeenCalled();
expect(reloadOnboardingPluginRegistry).not.toHaveBeenCalled();
expect(loadChannelSetupPluginRegistrySnapshotForChannel).not.toHaveBeenCalled();
expect(reloadChannelSetupPluginRegistry).not.toHaveBeenCalled();
});
it("shows explicit dmScope config command in channel primer", async () => {
@ -356,7 +356,7 @@ describe("setupChannels", () => {
},
} satisfies ChannelPluginCatalogEntry,
]);
vi.mocked(loadOnboardingPluginRegistrySnapshotForChannel).mockImplementation(
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockImplementation(
({ channel }: { channel: string }) => {
const registry = createEmptyPluginRegistry();
if (channel === "msteams") {
@ -418,7 +418,7 @@ describe("setupChannels", () => {
prompter,
);
expect(loadOnboardingPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect(loadChannelSetupPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect.objectContaining({
channel: "msteams",
pluginId: "@openclaw/msteams-plugin",
@ -454,7 +454,7 @@ describe("setupChannels", () => {
],
diagnostics: [],
});
vi.mocked(loadOnboardingPluginRegistrySnapshotForChannel).mockImplementation(
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockImplementation(
({ channel }: { channel: string }) => {
const registry = createEmptyPluginRegistry();
if (channel === "msteams") {
@ -511,8 +511,8 @@ describe("setupChannels", () => {
await runSetupChannels({} as OpenClawConfig, prompter);
expect(ensureOnboardingPluginInstalled).not.toHaveBeenCalled();
expect(loadOnboardingPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect(ensureChannelSetupPluginInstalled).not.toHaveBeenCalled();
expect(loadChannelSetupPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect.objectContaining({
channel: "msteams",
pluginId: "@openclaw/msteams-plugin",
@ -556,7 +556,7 @@ describe("setupChannels", () => {
},
}),
);
vi.mocked(loadOnboardingPluginRegistrySnapshotForChannel).mockImplementation(
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockImplementation(
({ channel }: { channel: string }) => {
const registry = createEmptyPluginRegistry();
if (channel === "msteams") {
@ -653,7 +653,7 @@ describe("setupChannels", () => {
{ allowDisable: true },
);
expect(loadOnboardingPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect(loadChannelSetupPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
expect.objectContaining({ channel: "msteams" }),
);
expect(setAccountEnabled).toHaveBeenCalledWith(

View File

@ -1,11 +1,11 @@
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import { listChannelPluginCatalogEntries } from "../channels/plugins/catalog.js";
import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js";
import type { ChannelSetupPlugin } from "../channels/plugins/setup-flow-types.js";
import {
getChannelSetupPlugin,
listChannelSetupPlugins,
} from "../channels/plugins/setup-registry.js";
import type { ChannelSetupPlugin } from "../channels/plugins/setup-wizard-types.js";
import {
formatChannelPrimerLine,
formatChannelSelectionLine,
@ -21,9 +21,13 @@ import type { RuntimeEnv } from "../runtime.js";
import { formatDocsLink } from "../terminal/links.js";
import type { WizardPrompter, WizardSelectOption } from "../wizard/prompts.js";
import { resolveChannelSetupEntries } from "./channel-setup/discovery.js";
import { resolveChannelSetupFlowAdapterForPlugin } from "./channel-setup/registry.js";
import {
ensureChannelSetupPluginInstalled,
loadChannelSetupPluginRegistrySnapshotForChannel,
} from "./channel-setup/plugin-install.js";
import { resolveChannelSetupWizardAdapterForPlugin } from "./channel-setup/registry.js";
import type {
ChannelSetupFlowAdapter,
ChannelSetupWizardAdapter,
ChannelSetupConfiguredResult,
ChannelSetupDmPolicy,
ChannelSetupResult,
@ -31,10 +35,6 @@ import type {
SetupChannelsOptions,
} from "./channel-setup/types.js";
import type { ChannelChoice } from "./onboard-types.js";
import {
ensureOnboardingPluginInstalled,
loadOnboardingPluginRegistrySnapshotForChannel,
} from "./onboarding/plugin-install.js";
type ConfiguredChannelAction = "update" | "disable" | "delete" | "skip";
@ -119,7 +119,7 @@ async function collectChannelStatus(params: {
options?: SetupChannelsOptions;
accountOverrides: Partial<Record<ChannelChoice, string>>;
installedPlugins?: ChannelSetupPlugin[];
resolveAdapter?: (channel: ChannelChoice) => ChannelSetupFlowAdapter | undefined;
resolveAdapter?: (channel: ChannelChoice) => ChannelSetupWizardAdapter | undefined;
}): Promise<ChannelStatusSummary> {
const installedPlugins = params.installedPlugins ?? listChannelSetupPlugins();
const workspaceDir = resolveAgentWorkspaceDir(params.cfg, resolveDefaultAgentId(params.cfg));
@ -131,7 +131,7 @@ async function collectChannelStatus(params: {
const resolveAdapter =
params.resolveAdapter ??
((channel: ChannelChoice) =>
resolveChannelSetupFlowAdapterForPlugin(
resolveChannelSetupWizardAdapterForPlugin(
installedPlugins.find((plugin) => plugin.id === channel),
));
const statusEntries = await Promise.all(
@ -271,7 +271,7 @@ async function maybeConfigureDmPolicies(params: {
selection: ChannelChoice[];
prompter: WizardPrompter;
accountIdsByChannel?: Map<ChannelChoice, string>;
resolveAdapter?: (channel: ChannelChoice) => ChannelSetupFlowAdapter | undefined;
resolveAdapter?: (channel: ChannelChoice) => ChannelSetupWizardAdapter | undefined;
}): Promise<OpenClawConfig> {
const { selection, prompter, accountIdsByChannel } = params;
const resolve = params.resolveAdapter ?? (() => undefined);
@ -374,7 +374,7 @@ export async function setupChannels(
if (existing) {
return existing;
}
const snapshot = loadOnboardingPluginRegistrySnapshotForChannel({
const snapshot = loadChannelSetupPluginRegistrySnapshotForChannel({
cfg: next,
runtime,
channel,
@ -393,9 +393,9 @@ export async function setupChannels(
const getVisibleSetupFlowAdapter = (channel: ChannelChoice) => {
const scopedPlugin = scopedPluginsById.get(channel);
if (scopedPlugin) {
return resolveChannelSetupFlowAdapterForPlugin(scopedPlugin);
return resolveChannelSetupWizardAdapterForPlugin(scopedPlugin);
}
return resolveChannelSetupFlowAdapterForPlugin(getChannelSetupPlugin(channel));
return resolveChannelSetupWizardAdapterForPlugin(getChannelSetupPlugin(channel));
};
const preloadConfiguredExternalPlugins = () => {
// Keep setup memory bounded by snapshot-loading only configured external plugins.
@ -735,7 +735,7 @@ export async function setupChannels(
const installedCatalogEntry = installedCatalogById.get(channel);
if (catalogEntry) {
const workspaceDir = resolveWorkspaceDir();
const result = await ensureOnboardingPluginInstalled({
const result = await ensureChannelSetupPluginInstalled({
cfg: next,
entry: catalogEntry,
prompter,

View File

@ -1,19 +1,19 @@
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import {
applyOnboardingLocalWorkspaceConfig,
applyLocalSetupWorkspaceConfig,
ONBOARDING_DEFAULT_DM_SCOPE,
ONBOARDING_DEFAULT_TOOLS_PROFILE,
} from "./onboard-config.js";
describe("applyOnboardingLocalWorkspaceConfig", () => {
describe("applyLocalSetupWorkspaceConfig", () => {
it("defaults local onboarding tool profile to coding", () => {
expect(ONBOARDING_DEFAULT_TOOLS_PROFILE).toBe("coding");
});
it("sets secure dmScope default when unset", () => {
const baseConfig: OpenClawConfig = {};
const result = applyOnboardingLocalWorkspaceConfig(baseConfig, "/tmp/workspace");
const result = applyLocalSetupWorkspaceConfig(baseConfig, "/tmp/workspace");
expect(result.session?.dmScope).toBe(ONBOARDING_DEFAULT_DM_SCOPE);
expect(result.gateway?.mode).toBe("local");
@ -27,7 +27,7 @@ describe("applyOnboardingLocalWorkspaceConfig", () => {
dmScope: "main",
},
};
const result = applyOnboardingLocalWorkspaceConfig(baseConfig, "/tmp/workspace");
const result = applyLocalSetupWorkspaceConfig(baseConfig, "/tmp/workspace");
expect(result.session?.dmScope).toBe("main");
});
@ -38,7 +38,7 @@ describe("applyOnboardingLocalWorkspaceConfig", () => {
dmScope: "per-account-channel-peer",
},
};
const result = applyOnboardingLocalWorkspaceConfig(baseConfig, "/tmp/workspace");
const result = applyLocalSetupWorkspaceConfig(baseConfig, "/tmp/workspace");
expect(result.session?.dmScope).toBe("per-account-channel-peer");
});
@ -49,7 +49,7 @@ describe("applyOnboardingLocalWorkspaceConfig", () => {
profile: "full",
},
};
const result = applyOnboardingLocalWorkspaceConfig(baseConfig, "/tmp/workspace");
const result = applyLocalSetupWorkspaceConfig(baseConfig, "/tmp/workspace");
expect(result.tools?.profile).toBe("full");
});

View File

@ -5,7 +5,7 @@ import type { ToolProfileId } from "../config/types.tools.js";
export const ONBOARDING_DEFAULT_DM_SCOPE: DmScope = "per-channel-peer";
export const ONBOARDING_DEFAULT_TOOLS_PROFILE: ToolProfileId = "coding";
export function applyOnboardingLocalWorkspaceConfig(
export function applyLocalSetupWorkspaceConfig(
baseConfig: OpenClawConfig,
workspaceDir: string,
): OpenClawConfig {

View File

@ -24,7 +24,7 @@ export async function setupInternalHooks(
const workspaceDir = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
const report = buildWorkspaceHookStatus(workspaceDir, { config: cfg });
// Show every eligible hook so users can opt in during onboarding.
// Show every eligible hook so users can opt in during setup.
const eligibleHooks = report.hooks.filter((h) => h.eligible);
if (eligibleHooks.length === 0) {

View File

@ -1,11 +1,11 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import type { RuntimeEnv } from "../runtime.js";
import { WizardCancelledError } from "../wizard/prompts.js";
import { runInteractiveOnboarding } from "./onboard-interactive.js";
import { runInteractiveSetup } from "./onboard-interactive.js";
const mocks = vi.hoisted(() => ({
createClackPrompter: vi.fn(() => ({ id: "prompter" })),
runOnboardingWizard: vi.fn(async () => {}),
runSetupWizard: vi.fn(async () => {}),
restoreTerminalState: vi.fn(),
}));
@ -13,8 +13,8 @@ vi.mock("../wizard/clack-prompter.js", () => ({
createClackPrompter: mocks.createClackPrompter,
}));
vi.mock("../wizard/onboarding.js", () => ({
runOnboardingWizard: mocks.runOnboardingWizard,
vi.mock("../wizard/setup.js", () => ({
runSetupWizard: mocks.runSetupWizard,
}));
vi.mock("../terminal/restore.js", () => ({
@ -29,7 +29,7 @@ function makeRuntime(): RuntimeEnv {
};
}
describe("runInteractiveOnboarding", () => {
describe("runInteractiveSetup", () => {
afterEach(() => {
vi.clearAllMocks();
});
@ -37,10 +37,10 @@ describe("runInteractiveOnboarding", () => {
it("restores terminal state without resuming stdin on success", async () => {
const runtime = makeRuntime();
await runInteractiveOnboarding({} as never, runtime);
await runInteractiveSetup({} as never, runtime);
expect(mocks.runOnboardingWizard).toHaveBeenCalledOnce();
expect(mocks.restoreTerminalState).toHaveBeenCalledWith("onboarding finish", {
expect(mocks.runSetupWizard).toHaveBeenCalledOnce();
expect(mocks.restoreTerminalState).toHaveBeenCalledWith("setup finish", {
resumeStdinIfPaused: false,
});
});
@ -54,12 +54,12 @@ describe("runInteractiveOnboarding", () => {
throw exitError;
}) as unknown as RuntimeEnv["exit"],
};
mocks.runOnboardingWizard.mockRejectedValueOnce(new WizardCancelledError("cancelled"));
mocks.runSetupWizard.mockRejectedValueOnce(new WizardCancelledError("cancelled"));
await expect(runInteractiveOnboarding({} as never, runtime)).rejects.toBe(exitError);
await expect(runInteractiveSetup({} as never, runtime)).rejects.toBe(exitError);
expect(runtime.exit).toHaveBeenCalledWith(1);
expect(mocks.restoreTerminalState).toHaveBeenCalledWith("onboarding finish", {
expect(mocks.restoreTerminalState).toHaveBeenCalledWith("setup finish", {
resumeStdinIfPaused: false,
});
const restoreOrder =
@ -73,12 +73,12 @@ describe("runInteractiveOnboarding", () => {
it("rethrows non-cancel errors after restoring terminal state", async () => {
const runtime = makeRuntime();
const err = new Error("boom");
mocks.runOnboardingWizard.mockRejectedValueOnce(err);
mocks.runSetupWizard.mockRejectedValueOnce(err);
await expect(runInteractiveOnboarding({} as never, runtime)).rejects.toThrow("boom");
await expect(runInteractiveSetup({} as never, runtime)).rejects.toThrow("boom");
expect(runtime.exit).not.toHaveBeenCalled();
expect(mocks.restoreTerminalState).toHaveBeenCalledWith("onboarding finish", {
expect(mocks.restoreTerminalState).toHaveBeenCalledWith("setup finish", {
resumeStdinIfPaused: false,
});
});

View File

@ -2,18 +2,18 @@ import type { RuntimeEnv } from "../runtime.js";
import { defaultRuntime } from "../runtime.js";
import { restoreTerminalState } from "../terminal/restore.js";
import { createClackPrompter } from "../wizard/clack-prompter.js";
import { runOnboardingWizard } from "../wizard/onboarding.js";
import { WizardCancelledError } from "../wizard/prompts.js";
import { runSetupWizard } from "../wizard/setup.js";
import type { OnboardOptions } from "./onboard-types.js";
export async function runInteractiveOnboarding(
export async function runInteractiveSetup(
opts: OnboardOptions,
runtime: RuntimeEnv = defaultRuntime,
) {
const prompter = createClackPrompter();
let exitCode: number | null = null;
try {
await runOnboardingWizard(opts, runtime, prompter);
await runSetupWizard(opts, runtime, prompter);
} catch (err) {
if (err instanceof WizardCancelledError) {
// Best practice: cancellation is not a successful completion.
@ -23,7 +23,7 @@ export async function runInteractiveOnboarding(
throw err;
} finally {
// Keep stdin paused so non-daemon runs can exit cleanly (e.g. Docker setup).
restoreTerminalState("onboarding finish", { resumeStdinIfPaused: false });
restoreTerminalState("setup finish", { resumeStdinIfPaused: false });
if (exitCode !== null) {
runtime.exit(exitCode);
}

View File

@ -90,7 +90,7 @@ vi.mock("../daemon/diagnostics.js", () => ({
readLastGatewayErrorLine: readLastGatewayErrorLineMock,
}));
const { runNonInteractiveOnboarding } = await import("./onboard-non-interactive.js");
const { runNonInteractiveSetup } = await import("./onboard-non-interactive.js");
const { resolveConfigPath: resolveStateConfigPath } = await import("../config/paths.js");
const { resolveConfigPath } = await import("../config/config.js");
const { callGateway } = await import("../gateway/call.js");
@ -170,7 +170,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
const token = "tok_test_123";
const workspace = path.join(stateDir, "openclaw");
await runNonInteractiveOnboarding(
await runNonInteractiveSetup(
{
nonInteractive: true,
mode: "local",
@ -208,7 +208,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
process.env.OPENCLAW_GATEWAY_TOKEN = envToken;
try {
await runNonInteractiveOnboarding(
await runNonInteractiveSetup(
{
nonInteractive: true,
mode: "local",
@ -248,7 +248,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
process.env.OPENCLAW_GATEWAY_TOKEN = envToken;
try {
await runNonInteractiveOnboarding(
await runNonInteractiveSetup(
{
nonInteractive: true,
mode: "local",
@ -292,7 +292,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
delete process.env.MISSING_GATEWAY_TOKEN_ENV;
try {
await expect(
runNonInteractiveOnboarding(
runNonInteractiveSetup(
{
nonInteractive: true,
mode: "local",
@ -322,7 +322,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
await withStateDir("state-remote-", async () => {
const port = getPseudoPort(30_000);
const token = "tok_remote_123";
await runNonInteractiveOnboarding(
await runNonInteractiveSetup(
{
nonInteractive: true,
mode: "remote",
@ -359,7 +359,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
}));
await expect(
runNonInteractiveOnboarding(
runNonInteractiveSetup(
{
nonInteractive: true,
mode: "local",
@ -386,7 +386,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
return { ok: true };
});
await runNonInteractiveOnboarding(
await runNonInteractiveSetup(
{
nonInteractive: true,
mode: "local",
@ -438,7 +438,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
try {
await expect(
runNonInteractiveOnboarding(
runNonInteractiveSetup(
{
nonInteractive: true,
mode: "local",
@ -509,7 +509,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
};
await expect(
runNonInteractiveOnboarding(
runNonInteractiveSetup(
{
nonInteractive: true,
mode: "local",
@ -568,7 +568,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
const port = getPseudoPort(40_000);
const workspace = path.join(stateDir, "openclaw");
await runNonInteractiveOnboarding(
await runNonInteractiveSetup(
{
nonInteractive: true,
mode: "local",

View File

@ -33,7 +33,7 @@ vi.mock("./zai-endpoint-detect.js", () => ({
detectZaiEndpoint,
}));
const { runNonInteractiveOnboarding } = await import("./onboard-non-interactive.js");
const { runNonInteractiveSetup } = await import("./onboard-non-interactive.js");
const NON_INTERACTIVE_DEFAULT_OPTIONS = {
nonInteractive: true,
@ -109,11 +109,11 @@ async function withOnboardEnv(
}
}
async function runNonInteractiveOnboardingWithDefaults(
async function runNonInteractiveSetupWithDefaults(
runtime: NonInteractiveRuntime,
options: Record<string, unknown>,
): Promise<void> {
await runNonInteractiveOnboarding(
await runNonInteractiveSetup(
{
...NON_INTERACTIVE_DEFAULT_OPTIONS,
...options,
@ -126,7 +126,7 @@ async function runOnboardingAndReadConfig(
env: OnboardEnv,
options: Record<string, unknown>,
): Promise<ProviderAuthConfigSnapshot> {
await runNonInteractiveOnboardingWithDefaults(env.runtime, {
await runNonInteractiveSetupWithDefaults(env.runtime, {
skipSkills: true,
...options,
});
@ -141,7 +141,7 @@ async function runCustomLocalNonInteractive(
runtime: NonInteractiveRuntime,
overrides: Record<string, unknown> = {},
): Promise<void> {
await runNonInteractiveOnboardingWithDefaults(runtime, {
await runNonInteractiveSetupWithDefaults(runtime, {
authChoice: "custom-api-key",
customBaseUrl: CUSTOM_LOCAL_BASE_URL,
customModelId: CUSTOM_LOCAL_MODEL_ID,
@ -366,7 +366,7 @@ describe("onboard (non-interactive): provider auth", () => {
const cleanToken = `sk-ant-oat01-${"a".repeat(80)}`;
const token = `${cleanToken.slice(0, 30)}\r${cleanToken.slice(30)}`;
await runNonInteractiveOnboardingWithDefaults(runtime, {
await runNonInteractiveSetupWithDefaults(runtime, {
authChoice: "token",
tokenProvider: "anthropic",
token,
@ -466,7 +466,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withEnvAsync(envOverrides, async () => {
let thrown: Error | undefined;
try {
await runNonInteractiveOnboardingWithDefaults(runtime, options);
await runNonInteractiveSetupWithDefaults(runtime, options);
} catch (error) {
thrown = error as Error;
}
@ -492,7 +492,7 @@ describe("onboard (non-interactive): provider auth", () => {
OPENCODE_ZEN_API_KEY: "opencode-zen-env-key", // pragma: allowlist secret
},
async () => {
await runNonInteractiveOnboardingWithDefaults(runtime, {
await runNonInteractiveSetupWithDefaults(runtime, {
authChoice: "opencode-zen",
secretInputMode: "ref", // pragma: allowlist secret
skipSkills: true,
@ -609,7 +609,7 @@ describe("onboard (non-interactive): provider auth", () => {
},
])("$name", async ({ prefix, options }) => {
await withOnboardEnv(prefix, async ({ configPath, runtime }) => {
await runNonInteractiveOnboardingWithDefaults(runtime, {
await runNonInteractiveSetupWithDefaults(runtime, {
cloudflareAiGatewayAccountId: "cf-account-id",
cloudflareAiGatewayGatewayId: "cf-gateway-id",
cloudflareAiGatewayApiKey: "cf-gateway-test-key", // pragma: allowlist secret
@ -689,7 +689,7 @@ describe("onboard (non-interactive): provider auth", () => {
it("configures a custom provider from non-interactive flags", async () => {
await withOnboardEnv("openclaw-onboard-custom-provider-", async ({ configPath, runtime }) => {
await runNonInteractiveOnboardingWithDefaults(runtime, {
await runNonInteractiveSetupWithDefaults(runtime, {
authChoice: "custom-api-key",
customBaseUrl: "https://llm.example.com/v1",
customApiKey: "custom-test-key", // pragma: allowlist secret
@ -713,7 +713,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv(
"openclaw-onboard-custom-provider-infer-",
async ({ configPath, runtime }) => {
await runNonInteractiveOnboardingWithDefaults(runtime, {
await runNonInteractiveSetupWithDefaults(runtime, {
customBaseUrl: "https://models.custom.local/v1",
customModelId: "local-large",
customApiKey: "custom-test-key", // pragma: allowlist secret
@ -810,7 +810,7 @@ describe("onboard (non-interactive): provider auth", () => {
"openclaw-onboard-custom-provider-invalid-compat-",
async ({ runtime }) => {
await expect(
runNonInteractiveOnboardingWithDefaults(runtime, {
runNonInteractiveSetupWithDefaults(runtime, {
authChoice: "custom-api-key",
customBaseUrl: "https://models.custom.local/v1",
customModelId: "local-large",
@ -825,7 +825,7 @@ describe("onboard (non-interactive): provider auth", () => {
it("fails custom provider auth when explicit provider id is invalid", async () => {
await withOnboardEnv("openclaw-onboard-custom-provider-invalid-id-", async ({ runtime }) => {
await expect(
runNonInteractiveOnboardingWithDefaults(runtime, {
runNonInteractiveSetupWithDefaults(runtime, {
authChoice: "custom-api-key",
customBaseUrl: "https://models.custom.local/v1",
customModelId: "local-large",
@ -843,7 +843,7 @@ describe("onboard (non-interactive): provider auth", () => {
"openclaw-onboard-custom-provider-missing-required-",
async ({ runtime }) => {
await expect(
runNonInteractiveOnboardingWithDefaults(runtime, {
runNonInteractiveSetupWithDefaults(runtime, {
customApiKey: "custom-test-key", // pragma: allowlist secret
skipSkills: true,
}),

View File

@ -28,19 +28,19 @@ export function createThrowingRuntime(): NonInteractiveRuntime {
};
}
export async function runNonInteractiveOnboarding(
export async function runNonInteractiveSetup(
options: Record<string, unknown>,
runtime: NonInteractiveRuntime,
): Promise<void> {
const { runNonInteractiveOnboarding: run } = await import("./onboard-non-interactive.js");
const { runNonInteractiveSetup: run } = await import("./onboard-non-interactive.js");
await run(options, runtime);
}
export async function runNonInteractiveOnboardingWithDefaults(
export async function runNonInteractiveSetupWithDefaults(
runtime: NonInteractiveRuntime,
options: Record<string, unknown>,
): Promise<void> {
await runNonInteractiveOnboarding(
await runNonInteractiveSetup(
{
...NON_INTERACTIVE_DEFAULT_OPTIONS,
...options,

View File

@ -3,18 +3,18 @@ import type { OpenClawConfig } from "../config/config.js";
import { readConfigFileSnapshot } from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import { defaultRuntime } from "../runtime.js";
import { runNonInteractiveOnboardingLocal } from "./onboard-non-interactive/local.js";
import { runNonInteractiveOnboardingRemote } from "./onboard-non-interactive/remote.js";
import { runNonInteractiveLocalSetup } from "./onboard-non-interactive/local.js";
import { runNonInteractiveRemoteSetup } from "./onboard-non-interactive/remote.js";
import type { OnboardOptions } from "./onboard-types.js";
export async function runNonInteractiveOnboarding(
export async function runNonInteractiveSetup(
opts: OnboardOptions,
runtime: RuntimeEnv = defaultRuntime,
) {
const snapshot = await readConfigFileSnapshot();
if (snapshot.exists && !snapshot.valid) {
runtime.error(
`Config invalid. Run \`${formatCliCommand("openclaw doctor")}\` to repair it, then re-run onboarding.`,
`Config invalid. Run \`${formatCliCommand("openclaw doctor")}\` to repair it, then re-run setup.`,
);
runtime.exit(1);
return;
@ -29,9 +29,9 @@ export async function runNonInteractiveOnboarding(
}
if (mode === "remote") {
await runNonInteractiveOnboardingRemote({ opts, runtime, baseConfig });
await runNonInteractiveRemoteSetup({ opts, runtime, baseConfig });
return;
}
await runNonInteractiveOnboardingLocal({ opts, runtime, baseConfig });
await runNonInteractiveLocalSetup({ opts, runtime, baseConfig });
}

View File

@ -4,7 +4,7 @@ import { resolveGatewayPort, writeConfigFile } from "../../config/config.js";
import { logConfigUpdated } from "../../config/logging.js";
import type { RuntimeEnv } from "../../runtime.js";
import { DEFAULT_GATEWAY_DAEMON_RUNTIME } from "../daemon-runtime.js";
import { applyOnboardingLocalWorkspaceConfig } from "../onboard-config.js";
import { applyLocalSetupWorkspaceConfig } from "../onboard-config.js";
import {
applyWizardMetadata,
DEFAULT_WORKSPACE,
@ -67,7 +67,7 @@ async function collectGatewayHealthFailureDiagnostics(): Promise<
: undefined;
}
export async function runNonInteractiveOnboardingLocal(params: {
export async function runNonInteractiveLocalSetup(params: {
opts: OnboardOptions;
runtime: RuntimeEnv;
baseConfig: OpenClawConfig;
@ -81,13 +81,13 @@ export async function runNonInteractiveOnboardingLocal(params: {
defaultWorkspaceDir: DEFAULT_WORKSPACE,
});
let nextConfig: OpenClawConfig = applyOnboardingLocalWorkspaceConfig(baseConfig, workspaceDir);
let nextConfig: OpenClawConfig = applyLocalSetupWorkspaceConfig(baseConfig, workspaceDir);
const inferredAuthChoice = inferAuthChoiceFromFlags(opts);
if (!opts.authChoice && inferredAuthChoice.matches.length > 1) {
runtime.error(
[
"Multiple API key flags were provided for non-interactive onboarding.",
"Multiple API key flags were provided for non-interactive setup.",
"Use a single provider flag or pass --auth-choice explicitly.",
`Flags: ${inferredAuthChoice.matches.map((match) => match.label).join(", ")}`,
].join("\n"),

View File

@ -6,7 +6,7 @@ import type { RuntimeEnv } from "../../runtime.js";
import { applyWizardMetadata } from "../onboard-helpers.js";
import type { OnboardOptions } from "../onboard-types.js";
export async function runNonInteractiveOnboardingRemote(params: {
export async function runNonInteractiveRemoteSetup(params: {
opts: OnboardOptions;
runtime: RuntimeEnv;
baseConfig: OpenClawConfig;

View File

@ -6,7 +6,7 @@ import { discoverGatewayBeacons } from "../infra/bonjour-discovery.js";
import { resolveWideAreaDiscoveryDomain } from "../infra/widearea-dns.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import {
promptSecretRefForOnboarding,
promptSecretRefForSetup,
resolveSecretInputModeForEnvSelection,
} from "./auth-choice.apply-helpers.js";
import { detectBinary } from "./onboard-helpers.js";
@ -175,7 +175,7 @@ export async function promptRemoteGatewayConfig(
},
});
if (selectedMode === "ref") {
const resolved = await promptSecretRefForOnboarding({
const resolved = await promptSecretRefForSetup({
provider: "gateway-remote-token",
config: cfg,
prompter,
@ -207,7 +207,7 @@ export async function promptRemoteGatewayConfig(
},
});
if (selectedMode === "ref") {
const resolved = await promptSecretRefForOnboarding({
const resolved = await promptSecretRefForSetup({
provider: "gateway-remote-password",
config: cfg,
prompter,

View File

@ -98,7 +98,7 @@ export type OnboardOptions = {
flow?: "quickstart" | "advanced" | "manual";
workspace?: string;
nonInteractive?: boolean;
/** Required for non-interactive onboarding; skips the interactive risk prompt when true. */
/** Required for non-interactive setup; skips the interactive risk prompt when true. */
acceptRisk?: boolean;
reset?: boolean;
resetScope?: ResetScope;
@ -111,7 +111,7 @@ export type OnboardOptions = {
tokenProfileId?: string;
/** Used when `authChoice=token` in non-interactive mode. */
tokenExpiresIn?: string;
/** API key persistence mode for onboarding flows (default: plaintext). */
/** API key persistence mode for setup flows (default: plaintext). */
secretInputMode?: SecretInputMode;
anthropicApiKey?: string;
openaiApiKey?: string;

View File

@ -3,18 +3,18 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import type { RuntimeEnv } from "../runtime.js";
const mocks = vi.hoisted(() => ({
runInteractiveOnboarding: vi.fn(async () => {}),
runNonInteractiveOnboarding: vi.fn(async () => {}),
runInteractiveSetup: vi.fn(async () => {}),
runNonInteractiveSetup: vi.fn(async () => {}),
readConfigFileSnapshot: vi.fn(async () => ({ exists: false, valid: false, config: {} })),
handleReset: vi.fn(async () => {}),
}));
vi.mock("./onboard-interactive.js", () => ({
runInteractiveOnboarding: mocks.runInteractiveOnboarding,
runInteractiveSetup: mocks.runInteractiveSetup,
}));
vi.mock("./onboard-non-interactive.js", () => ({
runNonInteractiveOnboarding: mocks.runNonInteractiveOnboarding,
runNonInteractiveSetup: mocks.runNonInteractiveSetup,
}));
vi.mock("../config/config.js", () => ({
@ -56,8 +56,8 @@ describe("onboardCommand", () => {
'Invalid --secret-input-mode. Use "plaintext" or "ref".',
);
expect(runtime.exit).toHaveBeenCalledWith(1);
expect(mocks.runInteractiveOnboarding).not.toHaveBeenCalled();
expect(mocks.runNonInteractiveOnboarding).not.toHaveBeenCalled();
expect(mocks.runInteractiveSetup).not.toHaveBeenCalled();
expect(mocks.runNonInteractiveSetup).not.toHaveBeenCalled();
});
it("logs ASCII-safe Windows guidance before onboarding", async () => {
@ -155,7 +155,7 @@ describe("onboardCommand", () => {
);
expect(runtime.exit).toHaveBeenCalledWith(1);
expect(mocks.handleReset).not.toHaveBeenCalled();
expect(mocks.runInteractiveOnboarding).not.toHaveBeenCalled();
expect(mocks.runNonInteractiveOnboarding).not.toHaveBeenCalled();
expect(mocks.runInteractiveSetup).not.toHaveBeenCalled();
expect(mocks.runNonInteractiveSetup).not.toHaveBeenCalled();
});
});

View File

@ -6,8 +6,8 @@ import { defaultRuntime } from "../runtime.js";
import { resolveUserPath } from "../utils.js";
import { isDeprecatedAuthChoice, normalizeLegacyOnboardAuthChoice } from "./auth-choice-legacy.js";
import { DEFAULT_WORKSPACE, handleReset } from "./onboard-helpers.js";
import { runInteractiveOnboarding } from "./onboard-interactive.js";
import { runNonInteractiveOnboarding } from "./onboard-non-interactive.js";
import { runInteractiveSetup } from "./onboard-interactive.js";
import { runNonInteractiveSetup } from "./onboard-non-interactive.js";
import type { OnboardOptions, ResetScope } from "./onboard-types.js";
const VALID_RESET_SCOPES = new Set<ResetScope>(["config", "config+creds+sessions", "full"]);
@ -56,7 +56,7 @@ export async function onboardCommand(opts: OnboardOptions, runtime: RuntimeEnv =
if (normalizedOpts.nonInteractive && normalizedOpts.acceptRisk !== true) {
runtime.error(
[
"Non-interactive onboarding requires explicit risk acknowledgement.",
"Non-interactive setup requires explicit risk acknowledgement.",
"Read: https://docs.openclaw.ai/security",
`Re-run with: ${formatCliCommand("openclaw onboard --non-interactive --accept-risk ...")}`,
].join("\n"),
@ -86,11 +86,11 @@ export async function onboardCommand(opts: OnboardOptions, runtime: RuntimeEnv =
}
if (normalizedOpts.nonInteractive) {
await runNonInteractiveOnboarding(normalizedOpts, runtime);
await runNonInteractiveSetup(normalizedOpts, runtime);
return;
}
await runInteractiveOnboarding(normalizedOpts, runtime);
await runInteractiveSetup(normalizedOpts, runtime);
}
export type { OnboardOptions } from "./onboard-types.js";

View File

@ -18,6 +18,7 @@ import {
pickGatewaySelfPresence,
resolveGatewayProbeAuthResolution,
} from "./status.gateway-probe.js";
import type { buildChannelsTable, collectChannelStatusIssues } from "./status.scan.runtime.js";
import { getStatusSummary } from "./status.summary.js";
import { getUpdateCheckResult } from "./status.update.js";

View File

@ -100,7 +100,7 @@ function mergeLegacyIntoDefaults(params: {
export const LEGACY_CONFIG_MIGRATIONS_PART_3: LegacyConfigMigration[] = [
{
// v2026.2.26 added a startup guard requiring gateway.controlUi.allowedOrigins (or the
// host-header fallback flag) for any non-loopback bind. The onboarding wizard was updated
// host-header fallback flag) for any non-loopback bind. The setup wizard was updated
// to seed this for new installs, but existing bind=lan/bind=custom installs that upgrade
// crash-loop immediately on next startup with no recovery path (issue #29385).
//

View File

@ -30,7 +30,7 @@ export const FIELD_HELP: Record<string, string> = {
"wizard.lastRunCommand":
"Command invocation recorded for the latest wizard run to preserve execution context. Use this to reproduce onboarding steps when verifying setup regressions.",
"wizard.lastRunMode":
'Wizard execution mode recorded as "local" or "remote" for the most recent onboarding flow. Use this to understand whether setup targeted direct local runtime or remote gateway topology.',
'Wizard execution mode recorded as "local" or "remote" for the most recent setup flow. Use this to understand whether setup targeted direct local runtime or remote gateway topology.',
diagnostics:
"Diagnostics controls for targeted tracing, telemetry export, and cache inspection during debugging. Keep baseline diagnostics minimal in production and enable deeper signals only when investigating issues.",
"diagnostics.otel":

View File

@ -172,7 +172,7 @@ function makeSymlinkStat(params?: { dev?: number; ino?: number }): import("node:
}
function mockWorkspaceStateRead(params: {
onboardingCompletedAt?: string;
setupCompletedAt?: string;
errorCode?: string;
rawContent?: string;
}) {
@ -186,7 +186,7 @@ function mockWorkspaceStateRead(params: {
return params.rawContent;
}
return JSON.stringify({
onboardingCompletedAt: params.onboardingCompletedAt ?? "2026-02-15T14:00:00.000Z",
setupCompletedAt: params.setupCompletedAt ?? "2026-02-15T14:00:00.000Z",
});
}
throw createEnoentError();
@ -513,7 +513,7 @@ describe("agents.files.list", () => {
});
it("hides BOOTSTRAP.md when workspace onboarding is complete", async () => {
mockWorkspaceStateRead({ onboardingCompletedAt: "2026-02-15T14:00:00.000Z" });
mockWorkspaceStateRead({ setupCompletedAt: "2026-02-15T14:00:00.000Z" });
const names = await listAgentFileNames();
expect(names).not.toContain("BOOTSTRAP.md");

View File

@ -16,7 +16,7 @@ import {
DEFAULT_TOOLS_FILENAME,
DEFAULT_USER_FILENAME,
ensureAgentWorkspace,
isWorkspaceOnboardingCompleted,
isWorkspaceSetupCompleted,
} from "../../agents/workspace.js";
import { movePathToTrash } from "../../browser/trash.js";
import {
@ -653,7 +653,7 @@ export const agentsHandlers: GatewayRequestHandlers = {
const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
let hideBootstrap = false;
try {
hideBootstrap = await isWorkspaceOnboardingCompleted(workspaceDir);
hideBootstrap = await isWorkspaceSetupCompleted(workspaceDir);
} catch {
// Fall back to showing BOOTSTRAP if workspace state cannot be read.
}

View File

@ -63,7 +63,7 @@ import {
prepareSecretsRuntimeSnapshot,
resolveCommandSecretsFromActiveRuntimeSnapshot,
} from "../secrets/runtime.js";
import { runOnboardingWizard } from "../wizard/onboarding.js";
import { runSetupWizard } from "../wizard/setup.js";
import { createAuthRateLimiter, type AuthRateLimiter } from "./auth-rate-limit.js";
import { startChannelHealthMonitor } from "./channel-health-monitor.js";
import { startGatewayConfigReloader } from "./config-reload.js";
@ -255,7 +255,7 @@ export type GatewayServerOptions = {
*/
allowCanvasHostInTests?: boolean;
/**
* Test-only: override the onboarding wizard runner.
* Test-only: override the setup wizard runner.
*/
wizardRunner?: (
opts: import("../commands/onboard-types.js").OnboardOptions,
@ -561,7 +561,7 @@ export async function startGatewayServer(
: { kind: "missing" };
}
const wizardRunner = opts.wizardRunner ?? runOnboardingWizard;
const wizardRunner = opts.wizardRunner ?? runSetupWizard;
const { wizardSessions, findRunningWizard, purgeWizardSession } = createWizardSessionTracker();
const deps = createDefaultDeps();

View File

@ -51,7 +51,7 @@ The LLM generates descriptive slugs based on your conversation:
## Requirements
- **Config**: `workspace.dir` must be set (automatically configured during onboarding)
- **Config**: `workspace.dir` must be set (automatically configured during setup)
The hook uses your configured LLM provider to generate slugs, so it works with any provider (Anthropic, OpenAI, etc.).

View File

@ -35,7 +35,7 @@ export {
addWildcardAllowFrom,
mergeAllowFromEntries,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../channels/plugins/setup-flow-helpers.js";
} from "../channels/plugins/setup-wizard-helpers.js";
export { PAIRING_APPROVED_MESSAGE } from "../channels/plugins/pairing-message.js";
export {
applyAccountNameToChannelSection,

Some files were not shown because too many files have changed in this diff Show More