fix: split search provider configure and switch flows
This commit is contained in:
parent
f4ea5221df
commit
98c5c04608
@ -237,7 +237,7 @@ describe("runConfigureWizard", () => {
|
|||||||
mocks.clackOutro.mockResolvedValue(undefined);
|
mocks.clackOutro.mockResolvedValue(undefined);
|
||||||
mocks.clackConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(true);
|
mocks.clackConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(true);
|
||||||
mocks.clackSelect.mockImplementation(async (params: { message: string }) => {
|
mocks.clackSelect.mockImplementation(async (params: { message: string }) => {
|
||||||
if (params.message === "Choose web search provider") {
|
if (params.message === "Choose active web search provider") {
|
||||||
return "tavily";
|
return "tavily";
|
||||||
}
|
}
|
||||||
if (params.message.startsWith("Search depth")) {
|
if (params.message.startsWith("Search depth")) {
|
||||||
@ -317,7 +317,7 @@ describe("runConfigureWizard", () => {
|
|||||||
mocks.clackOutro.mockResolvedValue(undefined);
|
mocks.clackOutro.mockResolvedValue(undefined);
|
||||||
mocks.clackConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(false);
|
mocks.clackConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(false);
|
||||||
mocks.clackSelect.mockImplementation(async (params: { message: string }) => {
|
mocks.clackSelect.mockImplementation(async (params: { message: string }) => {
|
||||||
if (params.message === "Choose web search provider") {
|
if (params.message === "Choose active web search provider") {
|
||||||
return "brave";
|
return "brave";
|
||||||
}
|
}
|
||||||
return "__continue";
|
return "__continue";
|
||||||
@ -411,7 +411,7 @@ describe("runConfigureWizard", () => {
|
|||||||
mocks.clackOutro.mockResolvedValue(undefined);
|
mocks.clackOutro.mockResolvedValue(undefined);
|
||||||
mocks.clackConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(true);
|
mocks.clackConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(true);
|
||||||
mocks.clackSelect.mockImplementation(async (params: { message: string }) => {
|
mocks.clackSelect.mockImplementation(async (params: { message: string }) => {
|
||||||
if (params.message === "Choose web search provider") {
|
if (params.message === "Choose active web search provider") {
|
||||||
return "tavily";
|
return "tavily";
|
||||||
}
|
}
|
||||||
if (params.message.startsWith("Search depth")) {
|
if (params.message.startsWith("Search depth")) {
|
||||||
@ -571,7 +571,7 @@ describe("runConfigureWizard", () => {
|
|||||||
mocks.clackOutro.mockResolvedValue(undefined);
|
mocks.clackOutro.mockResolvedValue(undefined);
|
||||||
mocks.clackConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(true);
|
mocks.clackConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(true);
|
||||||
mocks.clackSelect.mockImplementation(async (params: { message: string }) => {
|
mocks.clackSelect.mockImplementation(async (params: { message: string }) => {
|
||||||
if (params.message === "Choose web search provider") {
|
if (params.message === "Choose active web search provider") {
|
||||||
return "tavily";
|
return "tavily";
|
||||||
}
|
}
|
||||||
if (params.message.startsWith("Search depth")) {
|
if (params.message.startsWith("Search depth")) {
|
||||||
@ -784,7 +784,7 @@ describe("runConfigureWizard", () => {
|
|||||||
mocks.clackConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(true);
|
mocks.clackConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(true);
|
||||||
mocks.clackSelect.mockImplementation(
|
mocks.clackSelect.mockImplementation(
|
||||||
async (params: { message: string; options?: Array<{ value: string; hint?: string }> }) => {
|
async (params: { message: string; options?: Array<{ value: string; hint?: string }> }) => {
|
||||||
if (params.message === "Choose web search provider") {
|
if (params.message === "Choose active web search provider") {
|
||||||
expect(params.options?.[0]).toMatchObject({
|
expect(params.options?.[0]).toMatchObject({
|
||||||
value: "tavily",
|
value: "tavily",
|
||||||
hint: "Plugin search · External plugin",
|
hint: "Plugin search · External plugin",
|
||||||
|
|||||||
@ -166,7 +166,7 @@ describe("setupSearch", () => {
|
|||||||
await setupSearch(cfg, runtime, prompter);
|
await setupSearch(cfg, runtime, prompter);
|
||||||
|
|
||||||
const providerSelectCall = (prompter.select as ReturnType<typeof vi.fn>).mock.calls.find(
|
const providerSelectCall = (prompter.select as ReturnType<typeof vi.fn>).mock.calls.find(
|
||||||
(call) => call[0]?.message === "Choose web search provider",
|
(call) => call[0]?.message === "Choose active web search provider",
|
||||||
);
|
);
|
||||||
expect(providerSelectCall?.[0]).toEqual(
|
expect(providerSelectCall?.[0]).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@ -225,7 +225,7 @@ describe("setupSearch", () => {
|
|||||||
await setupSearch(cfg, runtime, prompter);
|
await setupSearch(cfg, runtime, prompter);
|
||||||
|
|
||||||
const providerSelectCall = (prompter.select as ReturnType<typeof vi.fn>).mock.calls.find(
|
const providerSelectCall = (prompter.select as ReturnType<typeof vi.fn>).mock.calls.find(
|
||||||
(call) => call[0]?.message === "Choose web search provider",
|
(call) => call[0]?.message === "Choose active web search provider",
|
||||||
);
|
);
|
||||||
expect(providerSelectCall?.[0]).toEqual(
|
expect(providerSelectCall?.[0]).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@ -294,7 +294,7 @@ describe("setupSearch", () => {
|
|||||||
await setupSearch(cfg, runtime, prompter);
|
await setupSearch(cfg, runtime, prompter);
|
||||||
|
|
||||||
const providerSelectCall = (prompter.select as ReturnType<typeof vi.fn>).mock.calls.find(
|
const providerSelectCall = (prompter.select as ReturnType<typeof vi.fn>).mock.calls.find(
|
||||||
(call) => call[0]?.message === "Choose web search provider",
|
(call) => call[0]?.message === "Choose active web search provider",
|
||||||
);
|
);
|
||||||
const matchingOptions =
|
const matchingOptions =
|
||||||
providerSelectCall?.[0]?.options?.filter(
|
providerSelectCall?.[0]?.options?.filter(
|
||||||
@ -359,7 +359,7 @@ describe("setupSearch", () => {
|
|||||||
|
|
||||||
expect(prompter.select).toHaveBeenCalledWith(
|
expect(prompter.select).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
message: "Choose web search provider",
|
message: "Choose active web search provider",
|
||||||
options: expect.arrayContaining([
|
options: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
value: "__install_plugin__",
|
value: "__install_plugin__",
|
||||||
@ -462,7 +462,7 @@ describe("setupSearch", () => {
|
|||||||
await setupSearch(cfg, runtime, prompter);
|
await setupSearch(cfg, runtime, prompter);
|
||||||
|
|
||||||
const options = (prompter.select as ReturnType<typeof vi.fn>).mock.calls.find(
|
const options = (prompter.select as ReturnType<typeof vi.fn>).mock.calls.find(
|
||||||
(call) => call[0]?.message === "Choose web search provider",
|
(call) => call[0]?.message === "Choose active web search provider",
|
||||||
)?.[0]?.options;
|
)?.[0]?.options;
|
||||||
expect(options[0]).toMatchObject({
|
expect(options[0]).toMatchObject({
|
||||||
value: "tavily",
|
value: "tavily",
|
||||||
@ -528,7 +528,7 @@ describe("setupSearch", () => {
|
|||||||
await setupSearch(cfg, runtime, prompter);
|
await setupSearch(cfg, runtime, prompter);
|
||||||
|
|
||||||
const configurePickerCall = (prompter.select as ReturnType<typeof vi.fn>).mock.calls.find(
|
const configurePickerCall = (prompter.select as ReturnType<typeof vi.fn>).mock.calls.find(
|
||||||
(call) => call[0]?.message === "Choose web search provider",
|
(call) => call[0]?.message === "Choose active web search provider",
|
||||||
);
|
);
|
||||||
expect(configurePickerCall?.[0]).toEqual(
|
expect(configurePickerCall?.[0]).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import {
|
|||||||
} from "./onboarding/plugin-install.js";
|
} from "./onboarding/plugin-install.js";
|
||||||
import {
|
import {
|
||||||
buildProviderSelectionOptions,
|
buildProviderSelectionOptions,
|
||||||
|
promptProviderManagementIntent,
|
||||||
type ProviderManagementIntent,
|
type ProviderManagementIntent,
|
||||||
} from "./provider-management.js";
|
} from "./provider-management.js";
|
||||||
import {
|
import {
|
||||||
@ -42,6 +43,8 @@ export type SearchProvider = string;
|
|||||||
const SEARCH_PROVIDER_INSTALL_SENTINEL = "__install_plugin__" as const;
|
const SEARCH_PROVIDER_INSTALL_SENTINEL = "__install_plugin__" as const;
|
||||||
const SEARCH_PROVIDER_KEEP_CURRENT_SENTINEL = "__keep_current__" as const;
|
const SEARCH_PROVIDER_KEEP_CURRENT_SENTINEL = "__keep_current__" as const;
|
||||||
const SEARCH_PROVIDER_SKIP_SENTINEL = "__skip__" as const;
|
const SEARCH_PROVIDER_SKIP_SENTINEL = "__skip__" as const;
|
||||||
|
const SEARCH_PROVIDER_CONFIGURE_SENTINEL = "__configure_provider__" as const;
|
||||||
|
const SEARCH_PROVIDER_SWITCH_ACTIVE_SENTINEL = "__switch_active_provider__" as const;
|
||||||
|
|
||||||
type PluginSearchProviderEntry = {
|
type PluginSearchProviderEntry = {
|
||||||
kind: "plugin";
|
kind: "plugin";
|
||||||
@ -1231,14 +1234,43 @@ export async function promptSearchProviderFlow(params: {
|
|||||||
includeSkipOption: params.includeSkipOption,
|
includeSkipOption: params.includeSkipOption,
|
||||||
skipHint: params.skipHint,
|
skipHint: params.skipHint,
|
||||||
});
|
});
|
||||||
|
const action = await promptProviderManagementIntent({
|
||||||
|
prompter: params.prompter,
|
||||||
|
message: "Web search setup",
|
||||||
|
includeSkipOption: params.includeSkipOption,
|
||||||
|
configuredCount: pickerModel.configuredCount,
|
||||||
|
configureValue: SEARCH_PROVIDER_CONFIGURE_SENTINEL,
|
||||||
|
switchValue: SEARCH_PROVIDER_SWITCH_ACTIVE_SENTINEL,
|
||||||
|
skipValue: SEARCH_PROVIDER_SKIP_SENTINEL,
|
||||||
|
configureLabel: "Configure or install a provider",
|
||||||
|
configureHint:
|
||||||
|
"Update keys, plugin settings, or install a provider without changing the active provider",
|
||||||
|
switchLabel: "Switch active provider",
|
||||||
|
switchHint: "Change which provider web_search uses right now",
|
||||||
|
skipHint: params.skipHint ?? "Configure later with openclaw configure --section web",
|
||||||
|
});
|
||||||
|
if (action === SEARCH_PROVIDER_SKIP_SENTINEL) {
|
||||||
|
return params.config;
|
||||||
|
}
|
||||||
|
const intent: SearchProviderFlowIntent =
|
||||||
|
action === SEARCH_PROVIDER_CONFIGURE_SENTINEL ? "configure-provider" : "switch-active";
|
||||||
const choice = await params.prompter.select<SearchProviderPickerChoice>({
|
const choice = await params.prompter.select<SearchProviderPickerChoice>({
|
||||||
message: "Choose web search provider",
|
message:
|
||||||
|
intent === "switch-active"
|
||||||
|
? "Choose active web search provider"
|
||||||
|
: "Choose provider to configure",
|
||||||
options: buildProviderSelectionOptions({
|
options: buildProviderSelectionOptions({
|
||||||
intent: "configure-provider",
|
intent,
|
||||||
options: pickerModel.options,
|
options: pickerModel.options,
|
||||||
activeValue: pickerModel.activeProvider,
|
activeValue: pickerModel.activeProvider,
|
||||||
|
hiddenValues: intent === "configure-provider" ? [SEARCH_PROVIDER_KEEP_CURRENT_SENTINEL] : [],
|
||||||
}),
|
}),
|
||||||
initialValue: pickerModel.initialValue,
|
initialValue:
|
||||||
|
intent === "switch-active"
|
||||||
|
? pickerModel.initialValue
|
||||||
|
: (pickerModel.options.find(
|
||||||
|
(option) => option.value !== SEARCH_PROVIDER_KEEP_CURRENT_SENTINEL,
|
||||||
|
)?.value ?? pickerModel.initialValue),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -1247,17 +1279,6 @@ export async function promptSearchProviderFlow(params: {
|
|||||||
) {
|
) {
|
||||||
return params.config;
|
return params.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedEntry = providerEntries.find((entry) => entry.value === choice);
|
|
||||||
const intent: SearchProviderFlowIntent =
|
|
||||||
choice === SEARCH_PROVIDER_INSTALL_SENTINEL
|
|
||||||
? "configure-provider"
|
|
||||||
: choice === pickerModel.activeProvider
|
|
||||||
? "configure-provider"
|
|
||||||
: selectedEntry?.configured
|
|
||||||
? "switch-active"
|
|
||||||
: "configure-provider";
|
|
||||||
|
|
||||||
return applySearchProviderChoice({
|
return applySearchProviderChoice({
|
||||||
config: params.config,
|
config: params.config,
|
||||||
choice,
|
choice,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user