refactor: rename setup wizard surfaces
This commit is contained in:
parent
07d71d2b27
commit
656848dcd7
@ -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";
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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!,
|
||||
});
|
||||
|
||||
@ -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!,
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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!,
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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!,
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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!,
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -96,7 +96,7 @@ const ollamaPlugin = {
|
||||
},
|
||||
},
|
||||
wizard: {
|
||||
onboarding: {
|
||||
setup: {
|
||||
choiceId: "ollama",
|
||||
choiceLabel: "Ollama",
|
||||
choiceHint: "Cloud and local open models",
|
||||
|
||||
@ -59,7 +59,7 @@ const sglangPlugin = {
|
||||
}),
|
||||
},
|
||||
wizard: {
|
||||
onboarding: {
|
||||
setup: {
|
||||
choiceId: "sglang",
|
||||
choiceLabel: "SGLang",
|
||||
choiceHint: "Fast self-hosted OpenAI-compatible server",
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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(" "),
|
||||
);
|
||||
}
|
||||
|
||||
@ -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 ?? [],
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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!,
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -59,7 +59,7 @@ const vllmPlugin = {
|
||||
}),
|
||||
},
|
||||
wizard: {
|
||||
onboarding: {
|
||||
setup: {
|
||||
choiceId: "vllm",
|
||||
choiceLabel: "vLLM",
|
||||
choiceHint: "Local/self-hosted OpenAI-compatible server",
|
||||
|
||||
@ -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!,
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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!,
|
||||
});
|
||||
|
||||
@ -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!,
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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!,
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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", () => {
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
@ -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,
|
||||
@ -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>;
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
},
|
||||
],
|
||||
|
||||
@ -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,
|
||||
},
|
||||
{
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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();
|
||||
});
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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",
|
||||
@ -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,
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../channels/plugins/setup-flow-types.js";
|
||||
export * from "../../channels/plugins/setup-wizard-types.js";
|
||||
|
||||
@ -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}`);
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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",
|
||||
});
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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");
|
||||
});
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,
|
||||
});
|
||||
});
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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,
|
||||
}),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 });
|
||||
}
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
@ -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).
|
||||
//
|
||||
|
||||
@ -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":
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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.
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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.).
|
||||
|
||||
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user