mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
fix(onboard): preserve auth for equivalent remote URLs
This commit is contained in:
@@ -201,6 +201,34 @@ describe("runNonInteractiveRemoteSetup", () => {
|
||||
expect(commit?.nextConfig.gateway?.remote).toEqual(remote);
|
||||
});
|
||||
|
||||
it("preserves endpoint-bound config when the remote URL has equivalent spelling", async () => {
|
||||
const existingRemote = {
|
||||
url: `${remoteUrl}/`,
|
||||
token: { source: "env" as const, provider: "default", id: "EXISTING_REMOTE_TOKEN" },
|
||||
tlsFingerprint: "sha256:test-fingerprint",
|
||||
sshTarget: "operator@gateway.example.test",
|
||||
edgeAuth: { "X-Edge-Auth": "existing-edge-secret" },
|
||||
};
|
||||
|
||||
await runNonInteractiveRemoteSetup({
|
||||
opts: {
|
||||
nonInteractive: true,
|
||||
mode: "remote",
|
||||
remoteUrl,
|
||||
secretInputMode: "ref",
|
||||
skipHooks: true,
|
||||
},
|
||||
runtime,
|
||||
baseConfig: { gateway: { mode: "remote", remote: existingRemote } },
|
||||
});
|
||||
|
||||
const commit = commitNonInteractiveOnboardConfigMock.mock.calls[0]?.[0];
|
||||
expect(commit?.nextConfig.gateway?.remote).toEqual({
|
||||
...existingRemote,
|
||||
url: remoteUrl,
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves an existing remote password SecretRef when no replacement is provided", async () => {
|
||||
const remote = {
|
||||
url: remoteUrl,
|
||||
|
||||
@@ -13,6 +13,7 @@ import { createGatewayEnvSecretRef } from "../../secrets/ref-contract.js";
|
||||
import { applySkipBootstrapConfig } from "../onboard-config.js";
|
||||
import { applyWizardMetadata } from "../onboard-helpers.js";
|
||||
import { enableDefaultOnboardingInternalHooks } from "../onboard-hooks.js";
|
||||
import { remoteGatewayUrlChanged } from "../onboard-remote-url.js";
|
||||
import type { OnboardOptions } from "../onboard-types.js";
|
||||
import { commitNonInteractiveOnboardConfig } from "./config-write.js";
|
||||
|
||||
@@ -54,7 +55,7 @@ export async function runNonInteractiveRemoteSetup(params: {
|
||||
return;
|
||||
}
|
||||
const existingRemote = baseConfig.gateway?.remote;
|
||||
const remoteUrlChanged = normalizeOptionalString(existingRemote?.url) !== remoteUrl;
|
||||
const remoteUrlChanged = remoteGatewayUrlChanged(remoteUrl, existingRemote?.url);
|
||||
// A remote block belongs to one endpoint. Reusing it for a different URL can
|
||||
// send old credentials or keep routing through the old SSH target.
|
||||
const preservedRemote = remoteUrlChanged ? {} : { ...existingRemote };
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { gatewayOriginScope } from "../../packages/gateway-client/src/gateway-origin-scope.js";
|
||||
|
||||
/** Returns whether an explicit remote Gateway URL selects a different endpoint. */
|
||||
export function remoteGatewayUrlChanged(
|
||||
nextUrl: string | undefined,
|
||||
previousUrl: string | undefined,
|
||||
): boolean {
|
||||
return (
|
||||
nextUrl !== undefined && gatewayOriginScope(nextUrl) !== gatewayOriginScope(previousUrl ?? "")
|
||||
);
|
||||
}
|
||||
@@ -1287,6 +1287,45 @@ describe("runSetupWizard", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("reuses stored remote credentials for an equivalent URL spelling", async () => {
|
||||
const storedRemote = {
|
||||
url: "wss://stored.example.com:18789",
|
||||
token: { source: "env" as const, provider: "default", id: "STORED_GATEWAY_TOKEN" },
|
||||
tlsFingerprint: "sha256:test-fingerprint",
|
||||
edgeAuth: { "X-Edge-Auth": "test-secret" },
|
||||
};
|
||||
readConfigFileSnapshot.mockResolvedValueOnce(
|
||||
configSnapshot({ gateway: { mode: "remote", remote: storedRemote } }),
|
||||
);
|
||||
|
||||
await runSetupWizard(
|
||||
{
|
||||
acceptRisk: true,
|
||||
flow: "advanced",
|
||||
mode: "remote",
|
||||
remoteUrl: `${storedRemote.url}/`,
|
||||
},
|
||||
createRuntime(),
|
||||
buildWizardPrompter({}),
|
||||
);
|
||||
|
||||
expect(promptRemoteGatewayConfig).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
gateway: expect.objectContaining({
|
||||
remote: {
|
||||
...storedRemote,
|
||||
url: `${storedRemote.url}/`,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
expect.any(Object),
|
||||
{
|
||||
secretInputMode: undefined,
|
||||
edgeAuthOriginUrl: storedRemote.url,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("does not probe an invalid CLI remote URL with its token", async () => {
|
||||
const remoteToken = "REDACTED";
|
||||
validateGatewayWebSocketUrl.mockReturnValueOnce("Use wss:// for public gateways");
|
||||
|
||||
+3
-3
@@ -4,6 +4,7 @@ import { listAgentEntries } from "../agents/agent-scope-config.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { resolveOnboardingSetupTarget } from "../commands/onboard-agent-target.js";
|
||||
import * as firstAgentOnboarding from "../commands/onboard-first-agent.js";
|
||||
import { remoteGatewayUrlChanged } from "../commands/onboard-remote-url.js";
|
||||
import type { OnboardMode, OnboardOptions } from "../commands/onboard-types.js";
|
||||
import { hasResolvedRosterBeforeMigrations } from "../config/agent-roster-provenance.js";
|
||||
import { ConfigMutationConflictError } from "../config/config.js";
|
||||
@@ -16,8 +17,7 @@ import {
|
||||
buildPluginCompatibilitySnapshotNotices,
|
||||
formatPluginCompatibilityNotice,
|
||||
} from "../plugins/status.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||
import { createLazyRuntimeModule } from "../shared/lazy-runtime.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import { t } from "./i18n/index.js";
|
||||
@@ -366,7 +366,7 @@ async function runSetupWizardOnce(
|
||||
const optionRemoteUrl = normalizeOptionalString(opts.remoteUrl);
|
||||
const optionRemoteToken = normalizeOptionalString(opts.remoteToken);
|
||||
const optionRemotePassword = normalizeOptionalString(opts.remotePassword);
|
||||
const remoteUrlChanged = opts.remoteUrl !== undefined && optionRemoteUrl !== storedRemoteUrl;
|
||||
const remoteUrlChanged = remoteGatewayUrlChanged(opts.remoteUrl, storedRemoteUrl);
|
||||
const remoteSeedConfig: OpenClawConfig =
|
||||
opts.remoteUrl === undefined &&
|
||||
opts.remoteToken === undefined &&
|
||||
|
||||
Reference in New Issue
Block a user