From 35256d6f1da876fa773e1e70327bb13cd848a5fb Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Fri, 3 Apr 2026 12:25:48 -0500 Subject: [PATCH] fix: honor nostr setup account label --- extensions/nostr/src/channel.test.ts | 28 ++++++++++++++++++ extensions/nostr/src/setup-surface.ts | 41 +++++++++++++++++---------- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/extensions/nostr/src/channel.test.ts b/extensions/nostr/src/channel.test.ts index c066616dcc2..921f1ab128e 100644 --- a/extensions/nostr/src/channel.test.ts +++ b/extensions/nostr/src/channel.test.ts @@ -203,6 +203,34 @@ describe("nostr setup wizard", () => { expect(result.cfg.channels?.nostr?.privateKey).toBe(TEST_HEX_PRIVATE_KEY); expect(result.cfg.channels?.nostr?.relays).toEqual(TEST_SETUP_RELAY_URLS); }); + + it("preserves the selected named account label during setup", async () => { + const prompter = createTestWizardPrompter({ + text: vi.fn(async ({ message }: { message: string }) => { + if (message === "Nostr private key (nsec... or hex)") { + return TEST_HEX_PRIVATE_KEY; + } + if (message === "Relay URLs (comma-separated, optional)") { + return ""; + } + throw new Error(`Unexpected prompt: ${message}`); + }) as WizardPrompter["text"], + }); + + const result = await runSetupWizardConfigure({ + configure: nostrConfigure, + cfg: {} as OpenClawConfig, + prompter, + options: {}, + accountOverrides: { + nostr: "work", + }, + }); + + expect(result.accountId).toBe("work"); + expect(result.cfg.channels?.nostr?.defaultAccount).toBe("work"); + expect(result.cfg.channels?.nostr?.privateKey).toBe(TEST_HEX_PRIVATE_KEY); + }); }); describe("nostr account helpers", () => { diff --git a/extensions/nostr/src/setup-surface.ts b/extensions/nostr/src/setup-surface.ts index c99b96a0e20..83684d7ec03 100644 --- a/extensions/nostr/src/setup-surface.ts +++ b/extensions/nostr/src/setup-surface.ts @@ -40,6 +40,13 @@ const NOSTR_ALLOW_FROM_HELP_LINES = [ `Docs: ${formatDocsLink("/channels/nostr", "channels/nostr")}`, ]; +function buildNostrSetupPatch(accountId: string, patch: Record) { + return { + ...(accountId !== DEFAULT_ACCOUNT_ID ? { defaultAccount: accountId } : {}), + ...patch, + }; +} + function parseRelayUrls(raw: string): { relays: string[]; error?: string } { const entries = splitSetupEntries(raw); const relays: string[] = []; @@ -89,12 +96,12 @@ const nostrDmPolicy: ChannelSetupDmPolicy = createTopLevelChannelDmPolicy({ }); export const nostrSetupAdapter: ChannelSetupAdapter = { - resolveAccountId: () => DEFAULT_ACCOUNT_ID, - applyAccountName: ({ cfg, name }) => + resolveAccountId: ({ accountId }) => accountId?.trim() || DEFAULT_ACCOUNT_ID, + applyAccountName: ({ cfg, accountId, name }) => patchTopLevelChannelConfigSection({ cfg, channel, - patch: name?.trim() ? { name: name.trim() } : {}, + patch: buildNostrSetupPatch(accountId, name?.trim() ? { name: name.trim() } : {}), }), validateInput: ({ input }) => { const typedInput = input as { @@ -118,7 +125,7 @@ export const nostrSetupAdapter: ChannelSetupAdapter = { } return null; }, - applyAccountConfig: ({ cfg, input }) => { + applyAccountConfig: ({ cfg, accountId, input }) => { const typedInput = input as { useEnv?: boolean; privateKey?: string; @@ -132,17 +139,18 @@ export const nostrSetupAdapter: ChannelSetupAdapter = { channel, enabled: true, clearFields: typedInput.useEnv ? ["privateKey"] : undefined, - patch: { + patch: buildNostrSetupPatch(accountId, { ...(typedInput.useEnv ? {} : { privateKey: typedInput.privateKey?.trim() }), ...(relayResult.relays.length > 0 ? { relays: relayResult.relays } : {}), - }, + }), }); }, }; export const nostrSetupWizard: ChannelSetupWizard = { channel, - resolveAccountIdForConfigure: () => DEFAULT_ACCOUNT_ID, + resolveAccountIdForConfigure: ({ accountOverride, defaultAccountId }) => + accountOverride?.trim() || defaultAccountId, resolveShouldPromptAccountIds: () => false, status: createStandardChannelSetupStatus({ channelLabel: "Nostr", @@ -170,13 +178,13 @@ export const nostrSetupWizard: ChannelSetupWizard = { accountId === DEFAULT_ACCOUNT_ID && Boolean(process.env.NOSTR_PRIVATE_KEY?.trim()) && !hasConfiguredSecretInput(resolveNostrAccount({ cfg, accountId }).config.privateKey), - apply: async ({ cfg }) => + apply: async ({ cfg, accountId }) => patchTopLevelChannelConfigSection({ cfg, channel, enabled: true, clearFields: ["privateKey"], - patch: {}, + patch: buildNostrSetupPatch(accountId, {}), }), }, credentials: [ @@ -200,20 +208,20 @@ export const nostrSetupWizard: ChannelSetupWizard = { envValue: process.env.NOSTR_PRIVATE_KEY?.trim(), }; }, - applyUseEnv: async ({ cfg }) => + applyUseEnv: async ({ cfg, accountId }) => patchTopLevelChannelConfigSection({ cfg, channel, enabled: true, clearFields: ["privateKey"], - patch: {}, + patch: buildNostrSetupPatch(accountId, {}), }), - applySet: async ({ cfg, resolvedValue }) => + applySet: async ({ cfg, accountId, resolvedValue }) => patchTopLevelChannelConfigSection({ cfg, channel, enabled: true, - patch: { privateKey: resolvedValue }, + patch: buildNostrSetupPatch(accountId, { privateKey: resolvedValue }), }), }, ], @@ -234,14 +242,17 @@ export const nostrSetupWizard: ChannelSetupWizard = { }, keepPrompt: (value) => `Relay URLs set (${value}). Keep them?`, validate: ({ value }) => parseRelayUrls(value).error, - applySet: async ({ cfg, value }) => { + applySet: async ({ cfg, accountId, value }) => { const relayResult = parseRelayUrls(value); return patchTopLevelChannelConfigSection({ cfg, channel, enabled: true, clearFields: relayResult.relays.length > 0 ? undefined : ["relays"], - patch: relayResult.relays.length > 0 ? { relays: relayResult.relays } : {}, + patch: buildNostrSetupPatch( + accountId, + relayResult.relays.length > 0 ? { relays: relayResult.relays } : {}, + ), }); }, },