mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 19:35:28 -06:00
fix(onboarding): honor classic gateway options (#112396)
* fix(onboarding): honor classic gateway options * test(wizard): type gateway setup mock * docs(onboarding): document quickstart gateway flags * chore(release): defer changelog entry
This commit is contained in:
committed by
GitHub
parent
685fb849bb
commit
1a121da22c
+6
-2
@@ -77,8 +77,12 @@ not overwrite the existing skill.
|
||||
|
||||
- `--classic`: opens the full step-by-step wizard. It cannot be combined with
|
||||
`--non-interactive`; omit `--classic` for automated setup.
|
||||
- `--flow quickstart`: opens the classic wizard with minimal prompts and
|
||||
auto-generates a gateway token.
|
||||
- `--flow quickstart`: opens the classic wizard with minimal prompts, uses
|
||||
token auth by default, and generates a token when no stored or explicit
|
||||
credential applies. Explicit local Gateway flags such as
|
||||
`--gateway-port`, `--gateway-bind`, `--gateway-auth`, and `--tailscale`
|
||||
override the corresponding stored or default quickstart values; omitted
|
||||
options keep their current values.
|
||||
- `--flow manual` (alias `advanced`): opens the classic wizard with full prompts
|
||||
for port, bind, and auth.
|
||||
- `--flow import`: runs a detected migration provider (for example Hermes via `--import-from hermes`), previews the plan, then applies after confirmation. When an interactive import supplies a default model, onboarding requires that route to pass a live completion before it skips provider setup; a failed imported route returns to provider configuration. Import only runs against a fresh OpenClaw setup - reset config, credentials, sessions, and workspace state first if any exist. Use [`openclaw migrate`](/cli/migrate) for dry-run plans, overwrite mode, reports, and exact mappings.
|
||||
|
||||
@@ -458,6 +458,53 @@ describe("setupWizardCommand", () => {
|
||||
expect(mocks.runNonInteractiveSetup).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("validates gateway token env refs before reset", async () => {
|
||||
const runtime = makeRuntime();
|
||||
|
||||
await setupWizardCommand(
|
||||
{
|
||||
reset: true,
|
||||
gatewayTokenRefEnv: "MISSING_GATEWAY_TOKEN_ENV",
|
||||
},
|
||||
runtime,
|
||||
);
|
||||
|
||||
expect(runtime.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Environment variable "MISSING_GATEWAY_TOKEN_ENV" is missing'),
|
||||
);
|
||||
expect(mocks.handleReset).not.toHaveBeenCalled();
|
||||
expect(mocks.runInteractiveSetup).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects conflicting gateway token inputs before reset", async () => {
|
||||
const previous = process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
process.env.OPENCLAW_GATEWAY_TOKEN = "env-token";
|
||||
const runtime = makeRuntime();
|
||||
|
||||
try {
|
||||
await setupWizardCommand(
|
||||
{
|
||||
reset: true,
|
||||
gatewayToken: "plaintext-token",
|
||||
gatewayTokenRefEnv: "OPENCLAW_GATEWAY_TOKEN",
|
||||
},
|
||||
runtime,
|
||||
);
|
||||
} finally {
|
||||
if (previous === undefined) {
|
||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
} else {
|
||||
process.env.OPENCLAW_GATEWAY_TOKEN = previous;
|
||||
}
|
||||
}
|
||||
|
||||
expect(runtime.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Use either --gateway-token or --gateway-token-ref-env"),
|
||||
);
|
||||
expect(mocks.handleReset).not.toHaveBeenCalled();
|
||||
expect(mocks.runInteractiveSetup).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("validates dependent auth-choice options before reset", async () => {
|
||||
const runtime = makeRuntime();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { formatInvalidPortOption } from "../cli/error-format.js";
|
||||
import { readConfigFileSnapshot, resolveGatewayPort } from "../config/config.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { isValidEnvSecretRefId } from "../config/types.secrets.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { assertSupportedRuntime } from "../infra/runtime-guard.js";
|
||||
import { resolveProviderMatch } from "../plugins/provider-auth-choice-helpers.js";
|
||||
@@ -102,6 +103,27 @@ function validatePreflightOptions(opts: OnboardOptions, runtime: RuntimeEnv): bo
|
||||
) {
|
||||
return rejectOption(runtime, formatInvalidPortOption("--gateway-port"));
|
||||
}
|
||||
if (opts.gatewayTokenRefEnv !== undefined) {
|
||||
const gatewayTokenRefEnv = opts.gatewayTokenRefEnv.trim();
|
||||
if (!isValidEnvSecretRefId(gatewayTokenRefEnv)) {
|
||||
return rejectOption(
|
||||
runtime,
|
||||
"Invalid --gateway-token-ref-env. Use an environment variable name like OPENCLAW_GATEWAY_TOKEN.",
|
||||
);
|
||||
}
|
||||
if (opts.gatewayToken !== undefined) {
|
||||
return rejectOption(
|
||||
runtime,
|
||||
"Use either --gateway-token or --gateway-token-ref-env, not both. Prefer --gateway-token-ref-env to avoid writing plaintext tokens.",
|
||||
);
|
||||
}
|
||||
if (!process.env[gatewayTokenRefEnv]?.trim()) {
|
||||
return rejectOption(
|
||||
runtime,
|
||||
`Environment variable "${gatewayTokenRefEnv}" is missing or empty. Export it first, then rerun ${formatCliCommand("openclaw onboard")}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (opts.nonInteractive && opts.mode === "remote" && !opts.remoteUrl?.trim()) {
|
||||
return rejectOption(
|
||||
runtime,
|
||||
|
||||
@@ -25,6 +25,7 @@ vi.mock("../infra/tailscale.js", () => ({
|
||||
}));
|
||||
|
||||
import { configureGatewayForSetup } from "./setup.gateway-config.js";
|
||||
import { resolveQuickstartGatewayDefaults } from "./setup.shared.js";
|
||||
|
||||
describe("configureGatewayForSetup", () => {
|
||||
function createPrompter(params: { selectQueue: string[]; textQueue: Array<string | undefined> }) {
|
||||
@@ -341,4 +342,78 @@ describe("configureGatewayForSetup", () => {
|
||||
expect(result.nextConfig.gateway?.auth?.token).toEqual(quickstartGateway.token);
|
||||
expect(result.settings.gatewayToken).toBe("token-from-exec");
|
||||
});
|
||||
|
||||
it("persists an explicit classic quickstart env token ref", async () => {
|
||||
const previous = process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
process.env.OPENCLAW_GATEWAY_TOKEN = "token-from-env-ref";
|
||||
try {
|
||||
const quickstartGateway = resolveQuickstartGatewayDefaults(
|
||||
{},
|
||||
{ gatewayTokenRefEnv: "OPENCLAW_GATEWAY_TOKEN" },
|
||||
);
|
||||
const result = await configureGatewayForSetup({
|
||||
flow: "quickstart",
|
||||
baseConfig: {},
|
||||
nextConfig: {},
|
||||
localPort: 18789,
|
||||
quickstartGateway,
|
||||
prompter: createPrompter({ selectQueue: [], textQueue: [] }),
|
||||
runtime: createRuntime(),
|
||||
});
|
||||
|
||||
expect(result.nextConfig.gateway?.auth).toEqual({
|
||||
mode: "token",
|
||||
token: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "OPENCLAW_GATEWAY_TOKEN",
|
||||
},
|
||||
});
|
||||
expect(result.settings.gatewayToken).toBe("token-from-env-ref");
|
||||
} finally {
|
||||
if (previous === undefined) {
|
||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
} else {
|
||||
process.env.OPENCLAW_GATEWAY_TOKEN = previous;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("persists classic quickstart overrides through gateway safety normalization", async () => {
|
||||
const password = ["classic", "gateway", "placeholder"].join("-");
|
||||
mocks.getTailnetHostname.mockResolvedValue("test-tailnet.ts.net");
|
||||
const note = vi.fn(async () => {});
|
||||
const prompter = buildWizardPrompter({ note });
|
||||
const quickstartGateway = resolveQuickstartGatewayDefaults(
|
||||
{},
|
||||
{
|
||||
gatewayPort: 19001,
|
||||
gatewayBind: "lan",
|
||||
gatewayAuth: "token",
|
||||
gatewayToken: "unused-token",
|
||||
gatewayPassword: password,
|
||||
tailscale: "funnel",
|
||||
tailscaleResetOnExit: true,
|
||||
},
|
||||
);
|
||||
|
||||
const result = await configureGatewayForSetup({
|
||||
flow: "quickstart",
|
||||
baseConfig: {},
|
||||
nextConfig: {},
|
||||
localPort: 18789,
|
||||
quickstartGateway,
|
||||
prompter,
|
||||
runtime: createRuntime(),
|
||||
});
|
||||
|
||||
expect(result.nextConfig.gateway).toMatchObject({
|
||||
port: 19001,
|
||||
bind: "loopback",
|
||||
auth: { mode: "password", password },
|
||||
tailscale: { mode: "funnel", resetOnExit: true },
|
||||
});
|
||||
expect(result.nextConfig.gateway?.auth?.token).toBeUndefined();
|
||||
expect(JSON.stringify(note.mock.calls)).not.toContain(password);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,105 @@ vi.mock("../plugins/install-record-commit.js", async (importOriginal) => ({
|
||||
commitConfigWriteWithPendingPluginInstalls: mocks.commitConfigWriteWithPendingPluginInstalls,
|
||||
}));
|
||||
|
||||
import { writeWizardConfigFile } from "./setup.shared.js";
|
||||
import { resolveQuickstartGatewayDefaults, writeWizardConfigFile } from "./setup.shared.js";
|
||||
|
||||
describe("resolveQuickstartGatewayDefaults", () => {
|
||||
const storedConfig: OpenClawConfig = {
|
||||
gateway: {
|
||||
port: 19111,
|
||||
bind: "custom",
|
||||
customBindHost: "192.0.2.10",
|
||||
auth: {
|
||||
mode: "token",
|
||||
token: "stored-token",
|
||||
password: "stored-password",
|
||||
},
|
||||
tailscale: {
|
||||
mode: "serve",
|
||||
resetOnExit: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it("overlays every explicitly supplied classic quickstart gateway option", () => {
|
||||
const result = resolveQuickstartGatewayDefaults(storedConfig, {
|
||||
gatewayPort: 19001,
|
||||
gatewayBind: "lan",
|
||||
gatewayAuth: "password",
|
||||
gatewayToken: "explicit-token",
|
||||
gatewayPassword: "explicit-password",
|
||||
tailscale: "off",
|
||||
tailscaleResetOnExit: false,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
hasExisting: true,
|
||||
port: 19001,
|
||||
bind: "lan",
|
||||
authMode: "password",
|
||||
tailscaleMode: "off",
|
||||
token: "explicit-token",
|
||||
password: "explicit-password",
|
||||
customBindHost: "192.0.2.10",
|
||||
tailscaleResetOnExit: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves stored quickstart defaults when no override is defined", () => {
|
||||
expect(resolveQuickstartGatewayDefaults(storedConfig)).toEqual({
|
||||
hasExisting: true,
|
||||
port: 19111,
|
||||
bind: "custom",
|
||||
authMode: "token",
|
||||
tailscaleMode: "serve",
|
||||
token: "stored-token",
|
||||
password: "stored-password",
|
||||
customBindHost: "192.0.2.10",
|
||||
tailscaleResetOnExit: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("aligns credential-only overrides while keeping an explicit auth mode authoritative", () => {
|
||||
expect(
|
||||
resolveQuickstartGatewayDefaults(storedConfig, {
|
||||
gatewayPassword: "explicit-password",
|
||||
}).authMode,
|
||||
).toBe("password");
|
||||
expect(
|
||||
resolveQuickstartGatewayDefaults(
|
||||
{ gateway: { auth: { mode: "password", password: "stored-password" } } },
|
||||
{ gatewayToken: "explicit-token" },
|
||||
).authMode,
|
||||
).toBe("token");
|
||||
expect(
|
||||
resolveQuickstartGatewayDefaults(storedConfig, {
|
||||
gatewayAuth: "password",
|
||||
gatewayToken: "explicit-token",
|
||||
}).authMode,
|
||||
).toBe("password");
|
||||
expect(
|
||||
resolveQuickstartGatewayDefaults(storedConfig, {
|
||||
gatewayAuth: "token",
|
||||
gatewayPassword: "explicit-password",
|
||||
}).authMode,
|
||||
).toBe("token");
|
||||
});
|
||||
|
||||
it("maps an explicit env-backed token to the canonical SecretRef", () => {
|
||||
expect(
|
||||
resolveQuickstartGatewayDefaults(storedConfig, {
|
||||
gatewayTokenRefEnv: " OPENCLAW_GATEWAY_TOKEN ",
|
||||
}),
|
||||
).toMatchObject({
|
||||
authMode: "token",
|
||||
token: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "OPENCLAW_GATEWAY_TOKEN",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("writeWizardConfigFile pending install ownership", () => {
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
stripPendingPluginInstallRecords,
|
||||
unchangedPendingPluginInstallRecordIds,
|
||||
} from "../plugins/install-record-commit.js";
|
||||
import { resolveDefaultSecretProviderAlias } from "../secrets/ref-contract.js";
|
||||
import { isPlainObject } from "../utils.js";
|
||||
import { t } from "./i18n/index.js";
|
||||
import { WizardCancelledError, type WizardPrompter } from "./prompts.js";
|
||||
@@ -19,6 +20,33 @@ import {
|
||||
} from "./setup.security-note.js";
|
||||
import type { QuickstartGatewayDefaults } from "./setup.types.js";
|
||||
|
||||
type QuickstartGatewayOptionOverrides = Pick<
|
||||
OnboardOptions,
|
||||
| "gatewayPort"
|
||||
| "gatewayBind"
|
||||
| "gatewayAuth"
|
||||
| "gatewayToken"
|
||||
| "gatewayTokenRefEnv"
|
||||
| "gatewayPassword"
|
||||
| "tailscale"
|
||||
| "tailscaleResetOnExit"
|
||||
>;
|
||||
|
||||
export function hasQuickstartGatewayOverrides(
|
||||
overrides: QuickstartGatewayOptionOverrides,
|
||||
): boolean {
|
||||
return (
|
||||
overrides.gatewayPort !== undefined ||
|
||||
overrides.gatewayBind !== undefined ||
|
||||
overrides.gatewayAuth !== undefined ||
|
||||
overrides.gatewayToken !== undefined ||
|
||||
overrides.gatewayTokenRefEnv !== undefined ||
|
||||
overrides.gatewayPassword !== undefined ||
|
||||
overrides.tailscale !== undefined ||
|
||||
overrides.tailscaleResetOnExit !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
function mergeWizardConfigValueOntoLatest(current: unknown, base: unknown, next: unknown): unknown {
|
||||
if (isDeepStrictEqual(next, base)) {
|
||||
return current;
|
||||
@@ -171,6 +199,7 @@ function applySecurityAcknowledgement(config: OpenClawConfig): OpenClawConfig {
|
||||
/** Derive quickstart gateway defaults, preserving any existing gateway settings. */
|
||||
export function resolveQuickstartGatewayDefaults(
|
||||
baseConfig: OpenClawConfig,
|
||||
overrides: QuickstartGatewayOptionOverrides = {},
|
||||
): QuickstartGatewayDefaults {
|
||||
const hasExisting =
|
||||
typeof baseConfig.gateway?.port === "number" ||
|
||||
@@ -206,15 +235,33 @@ export function resolveQuickstartGatewayDefaults(
|
||||
? tailscaleRaw
|
||||
: "off";
|
||||
|
||||
const explicitAuthMode =
|
||||
overrides.gatewayAuth ??
|
||||
(overrides.gatewayToken !== undefined || overrides.gatewayTokenRefEnv !== undefined
|
||||
? "token"
|
||||
: overrides.gatewayPassword !== undefined
|
||||
? "password"
|
||||
: undefined);
|
||||
|
||||
return {
|
||||
hasExisting,
|
||||
port: resolveGatewayPort(baseConfig),
|
||||
bind,
|
||||
authMode,
|
||||
tailscaleMode,
|
||||
token: baseConfig.gateway?.auth?.token,
|
||||
password: baseConfig.gateway?.auth?.password,
|
||||
port: overrides.gatewayPort ?? resolveGatewayPort(baseConfig),
|
||||
bind: overrides.gatewayBind ?? bind,
|
||||
authMode: explicitAuthMode ?? authMode,
|
||||
tailscaleMode: overrides.tailscale ?? tailscaleMode,
|
||||
token:
|
||||
overrides.gatewayTokenRefEnv !== undefined
|
||||
? {
|
||||
source: "env",
|
||||
provider: resolveDefaultSecretProviderAlias(baseConfig, "env", {
|
||||
preferFirstProviderForSource: true,
|
||||
}),
|
||||
id: overrides.gatewayTokenRefEnv.trim(),
|
||||
}
|
||||
: (overrides.gatewayToken ?? baseConfig.gateway?.auth?.token),
|
||||
password: overrides.gatewayPassword ?? baseConfig.gateway?.auth?.password,
|
||||
customBindHost: baseConfig.gateway?.customBindHost,
|
||||
tailscaleResetOnExit: baseConfig.gateway?.tailscale?.resetOnExit ?? false,
|
||||
tailscaleResetOnExit:
|
||||
overrides.tailscaleResetOnExit ?? baseConfig.gateway?.tailscale?.resetOnExit ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ type ApplyAuthChoice = typeof import("../commands/auth-choice.js").applyAuthChoi
|
||||
type PrepareAuthChoice = typeof import("../commands/auth-choice.js").prepareAuthChoice;
|
||||
type VerifySetupInferenceConfig =
|
||||
typeof import("../system-agent/setup-inference.js").verifySetupInferenceConfig;
|
||||
type ConfigureGatewayForSetup = typeof import("./setup.gateway-config.js").configureGatewayForSetup;
|
||||
|
||||
const ensureAuthProfileStore = vi.hoisted(() => vi.fn(() => ({ profiles: {} })));
|
||||
const keepCurrentAuthChoice = vi.hoisted(() => "__keep-current" as const);
|
||||
@@ -63,7 +64,7 @@ const applyPrimaryModel = vi.hoisted(() => vi.fn((cfg) => cfg));
|
||||
const promptDefaultModel = vi.hoisted(() => vi.fn<PromptDefaultModel>(async () => ({})));
|
||||
const promptCustomApiConfig = vi.hoisted(() => vi.fn(async (args) => ({ config: args.config })));
|
||||
const configureGatewayForSetup = vi.hoisted(() =>
|
||||
vi.fn(async (args) => ({
|
||||
vi.fn<ConfigureGatewayForSetup>(async (args) => ({
|
||||
nextConfig: args.nextConfig,
|
||||
settings: {
|
||||
port: args.localPort ?? 18789,
|
||||
@@ -2104,6 +2105,99 @@ describe("runSetupWizard", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("persists explicit classic quickstart gateway options without printing the password", async () => {
|
||||
const password = ["classic", "password", "placeholder"].join("-");
|
||||
const note: WizardPrompter["note"] = vi.fn(async () => {});
|
||||
const prompter = buildWizardPrompter({ note });
|
||||
const runtime = createRuntime();
|
||||
readConfigFileSnapshot.mockResolvedValueOnce(
|
||||
configSnapshot({
|
||||
gateway: {
|
||||
port: 19111,
|
||||
bind: "loopback",
|
||||
auth: { mode: "token", token: "stored-token" },
|
||||
tailscale: { mode: "off", resetOnExit: false },
|
||||
},
|
||||
}),
|
||||
);
|
||||
replaceConfigFile.mockClear();
|
||||
configureGatewayForSetup.mockImplementationOnce(async (args) => ({
|
||||
nextConfig: {
|
||||
...args.nextConfig,
|
||||
gateway: {
|
||||
...args.nextConfig.gateway,
|
||||
port: args.quickstartGateway.port,
|
||||
bind: args.quickstartGateway.bind,
|
||||
auth: {
|
||||
...args.nextConfig.gateway?.auth,
|
||||
mode: args.quickstartGateway.authMode,
|
||||
password: args.quickstartGateway.password,
|
||||
},
|
||||
tailscale: {
|
||||
...args.nextConfig.gateway?.tailscale,
|
||||
mode: args.quickstartGateway.tailscaleMode,
|
||||
resetOnExit: args.quickstartGateway.tailscaleResetOnExit,
|
||||
},
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
port: args.quickstartGateway.port,
|
||||
bind: args.quickstartGateway.bind,
|
||||
authMode: args.quickstartGateway.authMode,
|
||||
gatewayToken: undefined,
|
||||
tailscaleMode: args.quickstartGateway.tailscaleMode,
|
||||
tailscaleResetOnExit: args.quickstartGateway.tailscaleResetOnExit,
|
||||
},
|
||||
}));
|
||||
|
||||
await runSetupWizard(
|
||||
{
|
||||
acceptRisk: true,
|
||||
flow: "quickstart",
|
||||
mode: "local",
|
||||
authChoice: "skip",
|
||||
gatewayPort: 19001,
|
||||
gatewayBind: "lan",
|
||||
gatewayAuth: "password",
|
||||
gatewayPassword: password,
|
||||
installDaemon: false,
|
||||
skipChannels: true,
|
||||
skipSkills: true,
|
||||
skipSearch: true,
|
||||
skipHealth: true,
|
||||
skipUi: true,
|
||||
},
|
||||
runtime,
|
||||
prompter,
|
||||
);
|
||||
|
||||
const gatewaySetup = getMockCallArg(configureGatewayForSetup, 0, 0, "gateway setup");
|
||||
expect(requireRecord(gatewaySetup, "gateway setup").quickstartGateway).toMatchObject({
|
||||
port: 19001,
|
||||
bind: "lan",
|
||||
authMode: "password",
|
||||
password,
|
||||
});
|
||||
expect(
|
||||
persistedWizardConfigs().some(
|
||||
(config) =>
|
||||
config.gateway?.port === 19001 &&
|
||||
config.gateway.bind === "lan" &&
|
||||
config.gateway.auth?.mode === "password" &&
|
||||
config.gateway.auth.password === password,
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
const visibleOutput = [
|
||||
...getWizardNoteCalls(note).flat(),
|
||||
...((runtime.log as unknown as ReturnType<typeof vi.fn>).mock.calls.flat() as unknown[]),
|
||||
...((runtime.error as unknown as ReturnType<typeof vi.fn>).mock.calls.flat() as unknown[]),
|
||||
].join("\n");
|
||||
expect(visibleOutput).toContain("19001");
|
||||
expect(visibleOutput).not.toContain("Keeping your current gateway settings:");
|
||||
expect(visibleOutput).not.toContain(password);
|
||||
});
|
||||
|
||||
it("shows the resolved gateway port in quickstart for fresh envs", async () => {
|
||||
const previousPort = process.env.OPENCLAW_GATEWAY_PORT;
|
||||
process.env.OPENCLAW_GATEWAY_PORT = "18791";
|
||||
|
||||
+28
-32
@@ -26,6 +26,7 @@ import {
|
||||
import { runSetupModelAuthStep, type SetupModelAuthCandidate } from "./setup.model-auth.js";
|
||||
import { resolveSetupSecretInputString } from "./setup.secret-input.js";
|
||||
import {
|
||||
hasQuickstartGatewayOverrides,
|
||||
readSetupConfigFileSnapshot,
|
||||
readValidSetupConfigFile,
|
||||
requireRiskAcknowledgement,
|
||||
@@ -343,8 +344,13 @@ async function runSetupWizardOnce(
|
||||
flow = "quickstart";
|
||||
}
|
||||
const wizardFlow: WizardFlow = flow === "advanced" ? "advanced" : "quickstart";
|
||||
const hasExplicitQuickstartGatewayOverrides =
|
||||
wizardFlow === "quickstart" && hasQuickstartGatewayOverrides(opts);
|
||||
|
||||
const quickstartGateway: QuickstartGatewayDefaults = resolveQuickstartGatewayDefaults(baseConfig);
|
||||
const quickstartGateway: QuickstartGatewayDefaults = resolveQuickstartGatewayDefaults(
|
||||
baseConfig,
|
||||
wizardFlow === "quickstart" ? opts : undefined,
|
||||
);
|
||||
|
||||
if (flow === "quickstart") {
|
||||
const formatBind = (value: "loopback" | "lan" | "auto" | "custom" | "tailnet") => {
|
||||
@@ -371,37 +377,27 @@ async function runSetupWizardOnce(
|
||||
const formatTailscale = (value: "off" | "serve" | "funnel") => {
|
||||
return t(`wizard.gatewayTailscale.${value}`);
|
||||
};
|
||||
const quickstartLines = quickstartGateway.hasExisting
|
||||
? [
|
||||
t("wizard.setup.quickstartKeepSettings"),
|
||||
t("wizard.setup.quickstartGatewayPort", { port: quickstartGateway.port }),
|
||||
t("wizard.setup.quickstartGatewayBind", { bind: formatBind(quickstartGateway.bind) }),
|
||||
...(quickstartGateway.bind === "custom" && quickstartGateway.customBindHost
|
||||
? [
|
||||
t("wizard.setup.quickstartGatewayCustomIp", {
|
||||
host: quickstartGateway.customBindHost,
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
t("wizard.setup.quickstartGatewayAuth", {
|
||||
auth: formatAuth(quickstartGateway.authMode),
|
||||
}),
|
||||
t("wizard.setup.quickstartTailscaleExposure", {
|
||||
exposure: formatTailscale(quickstartGateway.tailscaleMode),
|
||||
}),
|
||||
t("wizard.setup.quickstartDirectChannels"),
|
||||
]
|
||||
: [
|
||||
t("wizard.setup.quickstartGatewayPort", { port: quickstartGateway.port }),
|
||||
t("wizard.setup.quickstartGatewayBind", { bind: t("wizard.gateway.bindLoopback") }),
|
||||
t("wizard.setup.quickstartGatewayAuth", {
|
||||
auth: t("wizard.setup.quickstartAuthTokenDefault"),
|
||||
}),
|
||||
t("wizard.setup.quickstartTailscaleExposure", {
|
||||
exposure: t("wizard.gatewayTailscale.off"),
|
||||
}),
|
||||
t("wizard.setup.quickstartDirectChannels"),
|
||||
];
|
||||
const quickstartLines = [
|
||||
...(quickstartGateway.hasExisting && !hasExplicitQuickstartGatewayOverrides
|
||||
? [t("wizard.setup.quickstartKeepSettings")]
|
||||
: []),
|
||||
t("wizard.setup.quickstartGatewayPort", { port: quickstartGateway.port }),
|
||||
t("wizard.setup.quickstartGatewayBind", { bind: formatBind(quickstartGateway.bind) }),
|
||||
...(quickstartGateway.bind === "custom" && quickstartGateway.customBindHost
|
||||
? [
|
||||
t("wizard.setup.quickstartGatewayCustomIp", {
|
||||
host: quickstartGateway.customBindHost,
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
t("wizard.setup.quickstartGatewayAuth", {
|
||||
auth: formatAuth(quickstartGateway.authMode),
|
||||
}),
|
||||
t("wizard.setup.quickstartTailscaleExposure", {
|
||||
exposure: formatTailscale(quickstartGateway.tailscaleMode),
|
||||
}),
|
||||
t("wizard.setup.quickstartDirectChannels"),
|
||||
];
|
||||
await prompter.note(quickstartLines.join("\n"), "QuickStart");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user